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 trialevgeniylischuk
5,265 PointsDoesn'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
25,541 PointsI 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;
}
}