Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android Implementing Designs for Android Customizing the Action Bar Using the Action Bar Style Generator

Doesn't work with AppCompat

Could, please, someone explain me how to make it work with Appcompat.Light. Especially I have problems with ActionBar in API 21+. It doesn't work at all.

I've tried to use generator with AppCompat theme selected but it doesn't work. Of course I can rebuild whole project with Holo.Light but as I understand it's very important to learn to work with support libraries because I want my projects work on both API21+ and API20-

And is there some reference (manual) about style xml attributes. All I can find is Material design

1 Answer

Bryner Toma
Bryner Toma
25,748 Points

I had a lot of trouble with this one. Posting for those who are frustrated. The method in the video of generating action bar styles and images is deprecated. This method uses AppCompat, Toolbar, TabLayout and @color/ resources instead. Happy coding :) teamtreehouse rocks.

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/main_content"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.smccspicy.ribbit.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/appbar_padding_top"
        android:theme="@style/Theme.Ribbit.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/Theme.Ribbit.PopupOverlay">

        </android.support.v7.widget.Toolbar>

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            style="@style/Theme.Ribbit.TabLayout"/>

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</android.support.design.widget.CoordinatorLayout>

styles.xml:

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <style name="Theme.Ribbit" parent="@style/Theme.AppCompat.Light.NoActionBar">
         <item name="editTextBackground">@drawable/apptheme_edit_text_holo_light</item>
    </style>

    <style name="Theme.Ribbit.AppBarOverlay" parent="ThemeOverlay.AppCompat.Light"/>
    <style name="Theme.Ribbit.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>

    <style name="Theme.Ribbit.TabLayout" parent="Widget.Design.TabLayout">
        <item name="tabIndicatorColor">@color/tab_indicator_color</item>
        <item name="tabBackground">@color/apptheme_color</item>
        <item name="tabTextAppearance">@style/TabTextAppearance</item>
        <item name="tabSelectedTextColor">@color/tab_text_color</item>
    </style>

    <style name="TabTextAppearance" parent="TextAppearance.AppCompat.Button">
        <item name="android:textColor">@color/tab_text_color</item>
    </style>

    <style name="AuthBackground">
        <item name="android:background">@drawable/background_fill</item>
    </style>

    <style name="AuthBackgroundImage">
        <item name="android:scaleType">fitStart</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">match_parent</item>
        <item name="android:layout_alignParentTop">true</item>
        <item name="android:layout_alignParentLeft">true</item>
        <item name="android:src">@drawable/background</item>
    </style>

    <style name="AuthTitle">
        <item name="android:layout_marginTop">32dp</item>
        <item name="android:textColor">@android:color/white</item>
        <item name="android:textSize">60sp</item>
        <item name="android:textStyle">bold</item>
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_alignParentTop">true</item>
        <item name="android:layout_centerHorizontal">true</item>
        <item name="android:text">@string/app_name</item>
    </style>

    <style name="AuthTitle.AuthSubtitle">
        <item name="android:textSize">13sp</item>
        <item name="android:layout_below">@+id/titleTextView</item>
        <item name="android:text">@string/subtitle</item>
        <item name="android:layout_alignParentTop">false</item>
        <item name="android:layout_marginTop">0dp</item>
    </style>

    <style name="AuthFieldContainer">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_below">@+id/subtitleTextView</item>
        <item name="android:layout_marginTop">@dimen/login_vertical_margin</item>
        <item name="android:layout_marginLeft">@dimen/activity_horizontal_margin</item>
        <item name="android:layout_marginRight">@dimen/activity_horizontal_margin</item>
        <item name="android:background">@android:color/white</item>
        <item name="android:orientation">vertical</item>
        <item name="android:paddingLeft">@dimen/login_horizontal_padding</item>
        <item name="android:paddingRight">@dimen/login_horizontal_padding</item>
        <item name="android:paddingTop">@dimen/login_vertical_padding</item>
        <item name="android:paddingBottom">@dimen/login_vertical_padding</item>
    </style>

    <style name="AuthEditText">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:textColorHint">@color/light_gray</item>
        <item name="android:textSize">17sp</item>
    </style>

    <style name="AuthButton">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_below">@+id/editTextLayout</item>
        <item name="android:layout_marginLeft">@dimen/activity_horizontal_margin</item>
        <item name="android:layout_marginRight">@dimen/activity_horizontal_margin</item>
        <item name="android:background">@drawable/button_custom</item>
        <item name="android:textColor">@color/text_color</item>
        <item name="android:textSize">13sp</item>
    </style>

    <style name="Theme.Ribbit.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

</resources>

In your AndroidManifest.xml:

<activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:theme="@style/Theme.Ribbit">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

MainActivity.java:

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.design.widget.TabLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;

import android.widget.ArrayAdapter;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.parse.FindCallback;
import com.parse.Parse;
import com.parse.ParseAnalytics;
import com.parse.ParseException;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.ParseUser;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    public static final String TAG = MainActivity.class.getSimpleName();

    public static final int TAKE_PHOTO_REQUEST = 0;
    public static final int TAKE_VIDEO_REQUEST = 1;
    public static final int CHOOSE_PICTURE_REQUEST = 2;
    public static final int CHOOSE_VIDEO_REQUEST = 3;

    public static final int MEDIA_TYPE_IMAGE = 4;
    public static final int MEDIA_TYPE_VIDEO = 5;

    public static final int FILE_SIZE_LIMIT = 1024*1024*10; //10MB

    protected Uri mMediaUri;

    protected DialogInterface.OnClickListener mDialogListener =
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    switch (which){
                        case 0:
                            Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                            mMediaUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
                            if(mMediaUri == null){
                                Toast.makeText(MainActivity.this, R.string.error_external_storage,
                                        Toast.LENGTH_LONG);
                            }
                            else {
                                takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mMediaUri);
                                startActivityForResult(takePhotoIntent, TAKE_PHOTO_REQUEST);
                            }
                            break;

                        case 1: // Take Video
                            Intent videoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                            mMediaUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO);
                            if(mMediaUri == null){
                                Toast.makeText(MainActivity.this, R.string.error_external_storage,
                                        Toast.LENGTH_LONG);
                            }
                            else {
                                videoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mMediaUri);
                                videoIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10);
                                videoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
                                startActivityForResult(videoIntent, TAKE_VIDEO_REQUEST);
                            }
                            break;

                        case 2: // Choose Picture
                            Intent choosePhotoIntent = new Intent(Intent.ACTION_GET_CONTENT);
                            choosePhotoIntent.setType("image/*");
                            startActivityForResult(choosePhotoIntent, CHOOSE_PICTURE_REQUEST);
                            break;
                        case 3: // Choose Video
                            Intent chooseVideoIntent = new Intent(Intent.ACTION_GET_CONTENT);
                            chooseVideoIntent.setType("video/*");
                            Toast.makeText(MainActivity.this,
                                    R.string.video_file_size_warning,
                                    Toast.LENGTH_LONG).show();

                            startActivityForResult(chooseVideoIntent, CHOOSE_VIDEO_REQUEST);
                            break;
                    }
                }

                private Uri getOutputMediaFileUri(int mediaType) {
                    //First check to see if external storage is available
                    if (isExternalStorageAvailable()){
                        //get Uri (path to store image at)

                        // 1. get external storage directory
                        String appName = MainActivity.this.getString(R.string.app_name);
                        File mediaStorageDir = new File(Environment
                                .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                                appName);
                        // 2. create our subdirectory
                        if(! mediaStorageDir.exists()){
                            if(!mediaStorageDir.mkdirs()){ //mkdirs returns a boolean when making a new directory
                                Log.e(TAG, "Failed to create directory.");
                                return null;
                            };
                        }
                        // 3. create a file name
                        // 4. create the file
                        File mediaFile;
                        Date now = new Date();
                        String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
                                Locale.CANADA).format(now);
                        String path = mediaStorageDir.getPath() + File.separator;
                        if(mediaType == MEDIA_TYPE_IMAGE){
                            mediaFile = new File(path + "IMG_" + timestamp + ".jpg");
                        }
                        else if(mediaType == MEDIA_TYPE_VIDEO){
                            mediaFile = new File(path + "VID_" + timestamp + ".mp4");
                        }
                        else {
                            return null;
                        }
                        Log.d(TAG, "File " + Uri.fromFile(mediaFile));

                        // 5. return the file's URI

                        return Uri.fromFile(mediaFile);
                    }
                    else{
                     return null;
                    }
                }

                private boolean isExternalStorageAvailable() {
                    String state = Environment.getExternalStorageState();
                    if(state.equals(Environment.MEDIA_MOUNTED)){
                        return true;
                    }
                    else {
                     return false;
                    }
                }
            };

    private SectionsPagerAdapter mSectionsPagerAdapter;
    //The {@link ViewPager} that will host the section contents.
    private ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ParseAnalytics.trackAppOpenedInBackground(getIntent());

        ParseUser currentUser = ParseUser.getCurrentUser();
        if (currentUser == null) {
            navigateToLogin();
        }
        else {
            Log.i(TAG, currentUser.getUsername());
        }

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // Create the adapter that will return a fragment for each
        // primary section of the activity.
        mSectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());

        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.container);
        mViewPager.setAdapter(mSectionsPagerAdapter);

        TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(mViewPager);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK){
            //broadcast to the devices gallery
            if (requestCode == CHOOSE_PICTURE_REQUEST || requestCode == CHOOSE_VIDEO_REQUEST){
                if(data == null){
                    Toast.makeText(this, R.string.general_error, Toast.LENGTH_LONG).show();
                }
                else {
                    mMediaUri = data.getData();
                }

                Log.i(TAG, "Media URI: " + mMediaUri);
                if (requestCode == CHOOSE_VIDEO_REQUEST){
                    // make sure video less that 10MB
                    int fileSize = 0;
                    InputStream inputStream = null;
                    try {
                        inputStream = getContentResolver().openInputStream(mMediaUri);
                        fileSize = inputStream.available();
                    }
                    catch (FileNotFoundException e){
                        Toast.makeText(MainActivity.this, R.string.error_opening_file, Toast.LENGTH_LONG).show();
                        return;
                    }
                    catch (IOException e){
                        Toast.makeText(MainActivity.this, R.string.error_opening_file, Toast.LENGTH_LONG).show();
                        return;
                    }
                    finally {
                        try {
                            inputStream.close();
                        }
                        catch (IOException e){
                            // Intentionally blank
                        }
                    }

                    if (fileSize >= FILE_SIZE_LIMIT){
                        Toast.makeText(this, R.string.error_file_size_too_large, Toast.LENGTH_LONG).show();
                        return;
                    }
                }
            }
            else {
                Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                mediaScanIntent.setData(mMediaUri);
                sendBroadcast(mediaScanIntent);
            }

            Intent recipientsIntent = new Intent(this, RecipientsActivity.class);
            recipientsIntent.setData(mMediaUri);
            String fileType;
            if(requestCode == TAKE_PHOTO_REQUEST || requestCode == CHOOSE_PICTURE_REQUEST){
                fileType = ParseConstants.TYPE_IMAGE;
            }
            else {
                fileType = ParseConstants.TYPE_VIDEO;
            }
            recipientsIntent.putExtra(ParseConstants.KEY_FILE_TYPE, fileType);
            startActivity(recipientsIntent);
        }
        else if (requestCode != RESULT_CANCELED){
            Toast.makeText(this, R.string.general_error, Toast.LENGTH_LONG).show();
        }
    }

    private void navigateToLogin() {
        Intent intent = new Intent(this, LoginActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intent);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        switch (id) {
            case R.id.action_logout:
                ParseUser.logOut();
                navigateToLogin();
                break;
            case R.id.action_edit_friends:
                Intent intent = new Intent(this, EditFriendsActivity.class);
                startActivity(intent);
                break;
            case R.id.action_camera:
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setItems(R.array.camera_choices, mDialogListener);
                AlertDialog dialog = builder.create();
                dialog.show();
                break;
        }

        return super.onOptionsItemSelected(item);
    }

}

SectionsPagerAdapter.java:

import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

/**
 * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
 * one of the sections/tabs/pages.
 */
public class SectionsPagerAdapter extends FragmentPagerAdapter {

    protected Context mContext;

    public SectionsPagerAdapter(Context context, FragmentManager fm) {
        super(fm);
        mContext = context;
    }

    @Override
    public Fragment getItem(int position) {
        // getItem is called to instantiate the fragment for the given page.
        // Return a PlaceholderFragment (defined as a static inner class below).
        switch(position){
            case 0:
                return new InboxFragment();
            case 1:
                return new FriendsFragment();
        }

        return null;
    }

    @Override
    public int getCount() {
        return 2;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        switch (position) {
            case 0:
                return mContext.getString(R.string.title_section1);
            case 1:
                return mContext.getString(R.string.title_section2);
        }
        return null;
    }
}