How to implement navigation drawer with fragments master detail - android

i have get the sample navigation drawer from this site :
http://www.androidhive.info/2013/11/android-sliding-menu-using-navigation-drawer/
and the master detail from here :
http://wptrafficanalyzer.in/blog/itemclick-handler-for-listfragment-in-android/
the error LogCat oncreateview(inflac....) the view
can not be created
me i have try that
//the main activiry as Activity:
package in.wptrafficanalyzer.listfragmentitemclick;
import in.wptrafficanalyzer.listfragmentitemclick.adapter.NavDrawerListAdapter;
import in.wptrafficanalyzer.listfragmentitemclick.model.NavDrawerItem;
import java.util.ArrayList;
import in.wptrafficanalyzer.listfragmentitemclick.R;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
public class MainActivity extends Activity implements CountryListFragment.ListFragmentItemClickListener {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;
// nav drawer title
private CharSequence mDrawerTitle;
// used to store app title
private CharSequence mTitle;
// slide menu items
private String[] navMenuTitles;
private TypedArray navMenuIcons;
private ArrayList<NavDrawerItem> navDrawerItems;
private NavDrawerListAdapter adapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTitle = mDrawerTitle = getTitle();
// load slide menu items
navMenuTitles = getResources().getStringArray(R.array.nav_drawer_items);
// nav drawer icons from resources
navMenuIcons = getResources()
.obtainTypedArray(R.array.nav_drawer_icons);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.list_slidermenu);
navDrawerItems = new ArrayList<NavDrawerItem>();
// adding nav drawer items to array
// Home
navDrawerItems.add(new NavDrawerItem(navMenuTitles[0], navMenuIcons.getResourceId(0, -1)));
// Find People
navDrawerItems.add(new NavDrawerItem(navMenuTitles[1], navMenuIcons.getResourceId(1, -1)));
// Photos
navDrawerItems.add(new NavDrawerItem(navMenuTitles[2], navMenuIcons.getResourceId(2, -1)));
// Communities, Will add a counter here
navDrawerItems.add(new NavDrawerItem(navMenuTitles[3], navMenuIcons.getResourceId(3, -1), true, "22"));
// Pages
navDrawerItems.add(new NavDrawerItem(navMenuTitles[4], navMenuIcons.getResourceId(4, -1)));
// What's hot, We will add a counter here
navDrawerItems.add(new NavDrawerItem(navMenuTitles[5], navMenuIcons.getResourceId(5, -1), true, "50+"));
// Recycle the typed array
navMenuIcons.recycle();
mDrawerList.setOnItemClickListener(new SlideMenuClickListener());
// setting the nav drawer list adapter
adapter = new NavDrawerListAdapter(getApplicationContext(),
navDrawerItems);
mDrawerList.setAdapter(adapter);
// enabling action bar app icon and behaving it as toggle button
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
R.drawable.ic_drawer, //nav menu toggle icon
R.string.app_name, // nav drawer open - description for accessibility
R.string.app_name // nav drawer close - description for accessibility
) {
public void onDrawerClosed(View view) {
getActionBar().setTitle(mTitle);
// calling onPrepareOptionsMenu() to show action bar icons
invalidateOptionsMenu();
}
public void onDrawerOpened(View drawerView) {
getActionBar().setTitle(mDrawerTitle);
// calling onPrepareOptionsMenu() to hide action bar icons
invalidateOptionsMenu();
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
if (savedInstanceState == null) {
// on first time display view for first nav item
displayView(0);
}
}
/**
* Slide menu item click listener
* */
private class SlideMenuClickListener implements
ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// display view for selected nav drawer item
displayView(position);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// toggle nav drawer on selecting action bar app icon/title
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
// Handle action bar actions click
switch (item.getItemId()) {
case R.id.action_settings:
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/***
* Called when invalidateOptionsMenu() is triggered
*/
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// if nav drawer is opened, hide the action items
boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
menu.findItem(R.id.action_settings).setVisible(!drawerOpen);
return super.onPrepareOptionsMenu(menu);
}
/**
* Diplaying fragment view for selected nav drawer list item
* */
private void displayView(int position) {
// update the main content by replacing fragments
ListFragment fragment = null;
switch (position) {
case 0:
//fragment = new HomeFragment();
break;
case 1:
fragment = new CountryListFragment();
break;
case 2:
//fragment = new PhotosFragment();
break;
case 3:
// fragment = new CommunityFragment();
break;
case 4:
//fragment = new PagesFragment();
break;
case 5:
//fragment = new WhatsHotFragment();
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.country_list_fragment, fragment).commit();
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
setTitle(navMenuTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
} else {
// error in creating fragment
Log.e("MainActivity", "Error in creating fragment");
}
}
#Override
public void setTitle(CharSequence title) {
mTitle = title;
getActionBar().setTitle(mTitle);
}
/**
* When using the ActionBarDrawerToggle, you must call it during
* onPostCreate() and onConfigurationChanged()...
*/
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggls
mDrawerToggle.onConfigurationChanged(newConfig);
}
/** Called when the activity is first created. */
#Override
public void onListFragmentItemClick(int position) {
/** Getting the orientation ( Landscape or Portrait ) of the screen */
int orientation = getResources().getConfiguration().orientation;
/** Landscape Mode */
if(orientation == Configuration.ORIENTATION_LANDSCAPE ){
/** Getting the fragment manager for fragment related operations */
FragmentManager fragmentManager = getFragmentManager();
/** Getting the fragmenttransaction object, which can be used to add, remove or replace a fragment */
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
/** Getting the existing detailed fragment object, if it already exists.
* The fragment object is retrieved by its tag name
* */
Fragment prevFrag = fragmentManager.findFragmentByTag("in.wptrafficanalyzer.country.details");
/** Remove the existing detailed fragment object if it exists */
if(prevFrag!=null)
fragmentTransaction.remove(prevFrag);
/** Instantiating the fragment CountryDetailsFragment */
CountryDetailsFragment fragment = new CountryDetailsFragment();
/** Creating a bundle object to pass the data(the clicked item's position) from the activity to the fragment */
Bundle b = new Bundle();
/** Setting the data to the bundle object */
b.putInt("position", position);
/** Setting the bundle object to the fragment */
fragment.setArguments(b);
/** Adding the fragment to the fragment transaction */
fragmentTransaction.add(R.id.detail_fragment_container, fragment,"in.wptrafficanalyzer.country.details");
/** Adding this transaction to backstack */
fragmentTransaction.addToBackStack(null);
/** Making this transaction in effect */
fragmentTransaction.commit();
}else{ /** Portrait Mode or Square mode */
/** Creating an intent object to start the CountryDetailsActivity */
Intent intent = new Intent("in.wptrafficanalyzer.CountryDetailsActivity");
/** Setting data ( the clicked item's position ) to this intent */
intent.putExtra("position", position);
/** Starting the activity by passing the implicit intent */
startActivity(intent);
}
}
}
the CountryListFragment as listfragment :
package in.wptrafficanalyzer.listfragmentitemclick;
import android.app.Activity;
import android.app.ListFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class CountryListFragment extends ListFragment{
/** List of countries to be displayed in the ListFragment */
ListFragmentItemClickListener ifaceItemClickListener;
/** An interface for defining the callback method */
public interface ListFragmentItemClickListener {
/** This method will be invoked when an item in the ListFragment is clicked */
void onListFragmentItemClick(int position);
}
/** A callback function, executed when this fragment is attached to an activity */
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try{
/** This statement ensures that the hosting activity implements ListFragmentItemClickListener */
ifaceItemClickListener = (ListFragmentItemClickListener) activity;
}catch(Exception e){
Toast.makeText(activity.getBaseContext(), "Exception",Toast.LENGTH_SHORT).show();
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/** Data source for the ListFragment */
ArrayAdapter<String> adapter = new ArrayAdapter<String>(inflater.getContext(), android.R.layout.simple_list_item_1, Country.name);
/** Setting the data source to the ListFragment */
setListAdapter(adapter);
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
/** Invokes the implementation of the method istFragmentItemClick in the hosting activity */
ifaceItemClickListener.onListFragmentItemClick(position);
}
}
layout main in folder layout
><android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/country_list_fragment"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:name="in.wptrafficanalyzer.listfragmentitemclick.CountryListFragment"
/>
<!-- Listview to display slider menu -->
<ListView
android:id="#+id/list_slidermenu"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#color/list_divider"
android:dividerHeight="1dp"
android:listSelector="#drawable/list_selector"
android:background="#color/list_background"/>
> </android.support.v4.widget.DrawerLayout>
layout main in the folder layout-land
><android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/country_list_fragment"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:name="in.wptrafficanalyzer.listfragmentitemclick.CountryListFragment"
/>
<FrameLayout
android:id="#+id/detail_fragment_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
/>
<!-- Listview to display slider menu -->
<ListView
android:id="#+id/list_slidermenu"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#color/list_divider"
android:dividerHeight="1dp"
android:listSelector="#drawable/list_selector"
android:background="#color/list_background"/>
></android.support.v4.widget.DrawerLayout>

I was confused by this, too. I set up the Navigation Drawer to navigate between major sections of my app. Then I wanted one of those major sections to be set up as Master/Detail. I knew it was possible, since this is basically what Gmail is, but was having a hard time putting what I had already built together with what Eclipse spits out when generating a Master/Detail Activity.
I couldn't just launch the ItemListFragment (Master/Detail) from the MainActivity (Navigation Drawer), because ItemListFragment wants to attach to ItemListActivity, not MainActivity, and a fragment can't have two Activities.
I finally found a tutorial on it that actually used the two design ideas together:
http://blog.evizija.si/android-layout/
I just followed their example and got my UI working as desired in about 5 minutes. It's actually incredibly simple. You make MasterActivity look a bit more like the generated ItemListActivity (ie. implement TaskListFragment.Callbacks, copy over the onItemSelected method, and a chuck of onCreate) and you're done!
I hope this helps solve your problem! Happy coding!
UPDATE: Elaborating on the steps involved (info from link), based on feedback in comments on improving my answer.
(1) Make your drawer activity and fragments, lets call them MainActivity and some fragments we don't care about here. Personally, I would create an empty Fragment, like ItemFragement for example, to be a place holder for the Master/Detail while I get the Drawer up and running. Then once the Drawer is working as desired, tackle the Master/Detail and linking them.
(2) Use the IDE wizard (I'm running Eclipse) to make the activities and fragments of master/detail flow. I'll refer to them by their default names: ItemListActivity, ItemListFragement, ItemDetailActivity, ItemDetailFragment.
(3) If you've used Master/Detail before, you know most of your logic goes into the fragment. This is still true when combining Master/Detail with Drawer. Note, at this point there is no connection between our MainActivity and our Master/Detail flow, and the later may not even be accessible in the UI.
(4) Key Concept: To connect the Drawer with the List, our MainActivity is going to be the hosting Activity for ItemListFragment (rather than the current ItemListActivity). To make this work, we just copy over some of the Master/Detail magic created by the wizard FROM ItemListActivity INTO MainActivity.
(5) Specifically:
(5A) MainActivity implements ItemListFragment.Callbacks (or EmployeeListFragment.Callbacks, AlbumListFragment.Callbacks, whatever you are listing) and implement onItemSelected method
public class MainActivity extends Activity
implements OnItemClickListener, ItemListFragment.Callbacks {
(5B) Copy part of code from onCreate in ItemListActivity and paste it to onCreate in MainActivity. This part:
if (findViewById(R.id.item_detail_container) != null) {
// The detail container view will be present only in the
// large-screen layouts (res/values-large and
// res/values-sw600dp). If this view is present, then the
// activity should be in two-pane mode.
mTwoPane = true;
// In two-pane mode, list items should be given the
// 'activated' state when touched.
((ItemListFragment) getFragmentManager()
.findFragmentById(R.id.item_list))
.setActivateOnItemClick(true);
}
(5C) Also copy onItemSelected method from ItemListActivity and paste it into MainActivity. You will already have an onItemSelected method if you told Eclipse to "add unimplemented methods" in response to the error that would have been raised after Step 5A. If you don't, copy over the whole method. (this step edited in response to a question in comments) Code:
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
Bundle arguments = new Bundle();
arguments.putString(ItemDetailFragment.ARG_ITEM_ID, id);
ItemDetailFragment fragment = new ItemDetailFragment();
fragment.setArguments(arguments);
getFragmentManager().beginTransaction()
.replace(R.id.item_detail_container, fragment)
.commit();
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, ItemDetailActivity.class);
detailIntent.putExtra(ItemDetailFragment.ARG_ITEM_ID, id);
startActivity(detailIntent);
}
(6) Then the last step is to have MainActivity (the Drawer) open ItemListFragment. If you already have a placeholder Fragment launching (like ItemFragement suggested in Step 1), this is just a matter of replacing ItemFragment with ItemListFragment in the onNavigationDrawerItemSelected method.
Hope that's clear. If not, the original link might do a better job explaining than I did. Just skim to the bottom where the blogger talks about adding the list activity to their drawer activity.
cheers.
UPDATE:
After being ask to do so by a moderator, I'm flagging this and another similar question (as duplicates).
Those questions:
https://stackoverflow.com/questions/25403377/combine-navigation-drawer-and-master-detail-layout
Navigation Drawer and master/detail flow

Related

Android: issue with menu item that are multiple checked

This is the beahviour of my App (only 1 is right, of course):
Of course I want only one item at the moment checked.
I divided the items in two groups (to add the divider, see my previous question: How add horizontal separator in navdrawer? )
This is the nav_menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single"
android:id="#+id/group1" >
<item
android:id="#+id/home"
android:checked="false"
android:icon="#drawable/ic_home_black_24dp"
android:title="#string/list_home" />
<item
android:id="#+id/list_event"
android:checked="false"
android:icon="#drawable/ic_list_black_24dp"
android:title="#string/list_event" />
</group>
<group
android:checkableBehavior="single"
android:id="#+id/group2" >
<item
android:id="#+id/settings"
android:checked="false"
android:icon="#drawable/ic_settings_black_24dp"
android:title="#string/settings" />
</group>
</menu>
This is the BaseApp that manage the NavDrawer:
package com.xx.views;
import android.support.design.widget.NavigationView;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.support.v7.app.ActionBarDrawerToggle;
import android.os.Bundle;
import android.view.View;
import com.xx.R;
import com.xx.mappers.DateManager;
public class BaseApp extends AppCompatActivity {
//Defining Variables
protected String LOGTAG = "LOGDEBUG";
protected Toolbar toolbar;
protected NavigationView navigationView;
protected DrawerLayout drawerLayout;
private DateManager db = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.base_layout);
navigationView = (NavigationView) findViewById(R.id.navigation_view);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame, new DashboardFragment());
fragmentTransaction.commit();
setNavDrawer();
// make home as checked
navigationView.getMenu().getItem(0).setChecked(true);
}
private void setNavDrawer(){
// Initializing Toolbar and setting it as the actionbar
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//Initializing NavigationView
//Setting Navigation View Item Selected Listener to handle the item click of the navigation menu
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
// This method will trigger on item Click of navigation menu
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
//Checking if the item is in checked state or not, if not make it in checked state
if (menuItem.isChecked()) menuItem.setChecked(false);
else menuItem.setChecked(true);
//Closing drawer on item click
drawerLayout.closeDrawers();
//Check to see which item was being clicked and perform appropriate action
switch (menuItem.getItemId()) {
case R.id.home:
DashboardFragment dashboardFragment = new DashboardFragment();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame, dashboardFragment,"DASHBOARD_FRAGMENT");
fragmentTransaction.commit();
return true;
case R.id.list_event:
ListEventFragment fragmentListEvent = new ListEventFragment();
fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame, fragmentListEvent);
fragmentTransaction.commit();
return true;
case R.id.settings:
SettingsFragment fragmentSettings = new SettingsFragment();
fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frame, fragmentSettings);
fragmentTransaction.commit();
return true;
default:
return true;
}
}
});
// Initializing Drawer Layout and ActionBarToggle
drawerLayout = (DrawerLayout) findViewById(R.id.drawer);
ActionBarDrawerToggle actionBarDrawerToggle =
new ActionBarDrawerToggle(this,drawerLayout,toolbar,R.string.open_drawer, R.string.close_drawer){
#Override
public void onDrawerClosed(View drawerView) {
// Code here will be triggered once the drawer closes as we dont want anything
// to happen so we leave this blank
super.onDrawerClosed(drawerView);
}
#Override
public void onDrawerOpened(View drawerView) {
// Code here will be triggered once the drawer open as we dont want anything
// to happen so we leave this blank
super.onDrawerOpened(drawerView);
}
};
//Setting the actionbarToggle to drawer layout
drawerLayout.setDrawerListener(actionBarDrawerToggle);
//calling sync state is necessay or else your hamburger icon wont show up
actionBarDrawerToggle.syncState();
}
private void eraseTable(){
db=new DateManager(this);
db.resetTable();
}
#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();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Thank you very much
This could help you: Try to removing the tag android:checkableBehavior="single" from xml and set android:checkable = “true” for each item individually, then declare a MenuItem object in the activity and in onNavigationItemSelected event if previously declared MenuItem object is not null then set checked value as false for it and then save current selected menuItem received as parameter to earlier declared MenuItem object.
This will set checked selection on even subitems.
if (prevMenuItem != null) {
prevMenuItem.setChecked(false);
}
menuItem.setChecked(true);
mDrawerLayout.closeDrawers();
prevMenuItem = menuItem;
return true;
I found this solutions here
If you are using groups and android:checkableBehavior="single", then all you need to do is set the single item as the checked item in the navigation view (not simply the item as checked with item.setChecked(true)):
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
//item.setChecked(true); //Won't work, will leave previous item checked too.
navigationView.setCheckedItem(id); //this will check single item
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
Now, whether you select or emulate select for an item, it will check only one item at a time.
Put both group inside single group an set
android:checkableBehavior="single"
Create a parent group
You would have to implement a custom adapter, custom/model, custom function to uncheck other items on click.
I suggest you to use this library. Its a material navigation drawer implementation, it already has all this logic implemented. If you dont want to go with a lib, them you should check how its done on this library code and adapt to your needs.

Implementing SwipeView, SwipeRefreshlayout, and Navigation Drawer in one activity

as the title suggests, I'm trying to implement all of these features at once. Originally I had a fully functioning side-nav with a SwipeRefreshLayout which holds a list view (using a custom list adapter). I then added a ViewPager inside of the SwipeRefreshLayout, and everything mostly works…except that as I swipe the list view does not appear in the new 'tabs' sometimes. Most of the time I see the first page and list view, I swipe right, nothing, I swipe right, nothing, I swipe left, list view, I swipe to the beginning, nothing.
I should add that everything is entirely dynamic, the navigation items are received from my server (which is also the number of pages) and each page has a custom list adapter with different list items. All of this dynamic information seems to be received and adapted correctly per page swipe etc.
Now for the code!
Drawer layout with swiperefreshlayout and view pager embedded. "drawer.xml"
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</android.support.v4.widget.SwipeRefreshLayout>
<!-- As the main content view, the view below consumes the entire
space available using match_parent in both dimensions. -->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<!-- android:layout_gravity="start" tells DrawerLayout to treat
this as a sliding drawer on the left side for left-to-right
languages and on the right side for right-to-left languages.
The drawer is given a fixed width in dp and extends the full height of
the container. A solid background is used for contrast
with the content view. -->
<ListView
android:id="#+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#color/grey_lighter"
android:dividerHeight="1dp"
android:background="#FFFFFF"/>
</android.support.v4.widget.DrawerLayout>
Listview which holds the custom list items created dynamically in the activity "dashboard.xml":
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/LV_dashboard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/textView1"
android:layout_alignRight="#+id/textView1"
android:layout_below="#+id/textView1" >
</ListView>
Now for the fragment activity :
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this.getApplicationContext();
navItems = new ArrayList<String>();
this.setContentView(R.layout.drawer);
mMerchantIds = Session.get().getMerchID(); //array retrieved from cache used for api
mCustomerPagerAdapter = new CustomerPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager, attaching the adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mCustomerPagerAdapter);
mTitle = mDrawerTitle = getTitle();
mNavTitles = getResources().getStringArray(R.array.nav_array); //I realize this isn't dynamic, I haven't gotten around to that just yet.
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
connection = new WiselyRequest();
// set a custom shadow that overlays the main content when the drawer opens
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
// set up the drawer's list view with items and click listener
mDrawerList.setAdapter(new ArrayAdapter<String>(this,
R.layout.nav_item, mNavTitles));
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
// enable ActionBar app icon to behave as action to toggle nav drawer
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);
// ActionBarDrawerToggle ties together the the proper interactions
// between the sliding drawer and the action bar app icon
mDrawerToggle = new ActionBarDrawerToggle(
this, /* host Activity */
mDrawerLayout, /* DrawerLayout object */
R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
R.string.drawer_open, /* "open drawer" description for accessibility */
R.string.drawer_close /* "close drawer" description for accessibility */
) {
public void onDrawerClosed(View view) {
getActionBar().setTitle(mTitle);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}
public void onDrawerOpened(View drawerView) {
getActionBar().setTitle(mDrawerTitle);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
if (savedInstanceState == null) {
selectItem(0);
}
}
This branches off into two parts, we'll start with the Drawer:
private class DrawerItemClickListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
}
private void selectItem(int position) {
// update the main content by replacing fragments (this is not what the pager does)
Fragment fragment = null;
fragment = new CustomerFragment();
Bundle args = new Bundle();
args.putString(KEY_MERCHANT_ID, (mMerchantIds.get(position)));
fragment.setArguments(args);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
setTitle(mNavTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
Here is the view pager:
public class CustomerPagerAdapter extends FragmentPagerAdapter {
public CustomerPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
Fragment fragment = new CustomerFragment();
Bundle args = new Bundle();
Log.d("Wisely", "merchants: "+mMerchantIds.get(i));
args.putString(KEY_MERCHANT_ID, (mMerchantIds.get(i))); // Our object is just an integer :-P
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
// For this contrived example, we have a 100-object collection.
return mMerchantIds.size(); //equal to the number of merchantss (that many customer objects)
}
#Override
public CharSequence getPageTitle(int position) {
return "OBJECT " + (position + 1);
}
}
Here is the customer fragment that both the drawer and pager are utilizing and where on refresh is handled:
public class CustomerFragment extends Fragment implements OnRefreshListener {
private View rootView;
SwipeRefreshLayout swipeLayout;
public CustomerFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Bundle args = getArguments();
String merchantID = args.getString(KEY_MERCHANT_ID);
if(connection.checkConnectivity(getApplicationContext())){
getRecentCustomers(API.getRecentCustomers()+"?merchant_id="+merchantID); //api call works totally fine, and correctly sets up the adapter
}
else
Toast.makeText(DashboardActivity.this, "Need to be connected to the internet!", 4000).show();
rootView = inflater.inflate(R.layout.dashboard, container, false);//inflates the dashboard
swipeLayout = (SwipeRefreshLayout)findViewById(R.id.swipe_container);
swipeLayout.setOnRefreshListener(this);
swipeLayout.setColorScheme(android.R.color.holo_blue_bright, android.R.color.holo_green_light, android.R.color.holo_orange_light, android.R.color.holo_red_light);
registerClickCallback(rootView);
return rootView;
}
#Override
public void onRefresh() {
if(connection.checkConnectivity(getApplicationContext()))
reloadActivity(); //literally turns the activity on and off without animation
else
Toast.makeText(DashboardActivity.this, "Need to be connected to the internet!", 4000).show();
}
I believe the issue exits in my layouts, not in the list adapter or customer fragment which seem to be working fine (every time I swipe to a new page the api is called and the correct data is put into the list view. I just can't see it. Also I realize that the navdrawer is not dynamic, but my bigger issue at the moment is being able to swipe through multiple list views on pages. When I change my customerFragment to inflate a simple text view with a number in it, it seems to work fine, except that the first page never goes away, other pages just pile on top of it (I think this has to do with the framelayout in the drawer but removing it breaks the code because my nag drawer relies on it). Any suggestions?
I think, you will have to create your own classes extending DrawerLayout or ViewPager.
Inside of your classes you have to Override methods:
onInterceptTouchEvent() - here you evaluate TouchEvent and return true, if you are intercepting it (not passing to the child View)
You have to tweak the conditions of intercepting based on what you are trying to achieve.
Guide is here:
http://developer.android.com/training/gestures/viewgroup.html

Weird "Could not dispatch event" and "No subscribers registered for event" issue

I've been trying to debug this thing for hours and I really can't see the issue here.
This is my MainActivity. The main thing to look for here is the ArrayList<Servico>, being Servico a custom object. I've created a simple "event" class - ServicoActual - that has just a Servico object reference (and a constructor/getter):
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.SearchManager;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import net.pedromoreira.billper.events.ServicoActual;
import java.util.ArrayList;
import java.util.Locale;
import de.greenrobot.event.EventBus;
public class MainActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;
private CharSequence mDrawerTitle;
private CharSequence mTitle;
private ArrayList<Servico> servicos;
//private String[] mPlanetTitles;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
servicos = new ArrayList<Servico>();
servicos.add(new Servico("Luz"));
servicos.add(new Servico("Água"));
servicos.add(new Servico("Gás"));
//Log.i("onCreate", "Servicos: " + servicos.size());
setContentView(R.layout.activity_main);
mTitle = mDrawerTitle = getTitle();
//mPlanetTitles = getResources().getStringArray(R.array.planets_array);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
// set a custom shadow that overlays the main content when the drawer opens
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
// set up the drawer's list view with items and click listener
mDrawerList.setAdapter(new ArrayAdapter<Servico>(this,
R.layout.drawer_list_item, servicos));
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
// enable ActionBar app icon to behave as action to toggle nav drawer
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);
// ActionBarDrawerToggle ties together the the proper interactions
// between the sliding drawer and the action bar app icon
mDrawerToggle = new ActionBarDrawerToggle(
this, /* host Activity */
mDrawerLayout, /* DrawerLayout object */
R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
R.string.drawer_open, /* "open drawer" description for accessibility */
R.string.drawer_close /* "close drawer" description for accessibility */
) {
public void onDrawerClosed(View view) {
getActionBar().setTitle(mTitle);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}
public void onDrawerOpened(View drawerView) {
getActionBar().setTitle(mDrawerTitle);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
if (savedInstanceState == null) {
selectItem(0);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}
/* Called whenever we call invalidateOptionsMenu() */
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// If the nav drawer is open, hide action items related to the content view
boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
return super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// The action bar home/up action should open or close the drawer.
// ActionBarDrawerToggle will take care of this.
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
// Handle action buttons
switch(item.getItemId()) {
case R.id.action_websearch:
// create intent to perform web search for this planet
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
intent.putExtra(SearchManager.QUERY, getActionBar().getTitle());
// catch event that there's no activity to handle intent
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/* The click listener for ListView in the navigation drawer */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
Log.i("DrawerItemClickListener", "Position: " + position);
}
}
private void selectItem(int position) {
// update the main content by replacing fragments
Fragment fragment = new ServicoFragment();
//Bundle args = new Bundle();
//args.putInt(ServicoFragment.ARG_PLANET_NUMBER, position);
//fragment.setArguments(args);
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
Log.i("selectItem", "Servico: " + servicos.get(position).getNome());
EventBus.getDefault().postSticky(new ServicoActual(servicos.get(position)));
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
setTitle(servicos.get(position).getNome());
mDrawerLayout.closeDrawer(mDrawerList);
}
#Override
public void setTitle(CharSequence title) {
mTitle = title;
getActionBar().setTitle(mTitle);
}
/**
* When using the ActionBarDrawerToggle, you must call it during
* onPostCreate() and onConfigurationChanged()...
*/
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggls
mDrawerToggle.onConfigurationChanged(newConfig);
}
/**
* Fragment that appears in the "content_frame"
*/
public static class ServicoFragment extends Fragment {
//public static final String ARG_PLANET_NUMBER = "planet_number";
private TextView mTestText;
public ServicoFragment() {
// Empty constructor required for fragment subclasses
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().registerSticky(this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_servico, container, false);
//int i = getArguments().getInt(ARG_PLANET_NUMBER);
//String planet = getResources().getStringArray(R.array.planets_array)[i];
//int imageId = getResources().getIdentifier(planet.toLowerCase(Locale.getDefault()),
// "drawable", getActivity().getPackageName());
//((ImageView) rootView.findViewById(R.id.image)).setImageResource(imageId);
//getActivity().setTitle(planet);
mTestText = ((TextView) rootView.findViewById(R.id.test_text));
Log.i("onCreateView", "mTestText: " + mTestText.toString());
mTestText.setText("xpto");
return rootView;
}
public void onEvent(ServicoActual e){
Servico servico = e.getServico();
Log.i("onEvent", "Servico: " + servico.getNome());
getActivity().setTitle(servico.getNome());
mTestText.setText(servico.getNome());
}
}
}
So, when a Drawer list item is clicked, I'm trying to pass the corresponding Servico (inside the ServicoActual "event") to the ServicoFragment, which should write the Servico's name to its TextView.
This is what happens with a single click on the first item (0):
07-13 00:50:35.388 26244-26244/net.pedromoreira.billper I/ViewRootImpl﹕ ViewRoot's Touch Event : Touch Down
07-13 00:50:35.628 26244-26244/net.pedromoreira.billper I/ViewRootImpl﹕ ViewRoot's Touch Event : Touch UP
07-13 00:50:36.308 26244-26244/net.pedromoreira.billper I/ViewRootImpl﹕ ViewRoot's Touch Event : Touch Down
07-13 00:50:36.358 26244-26244/net.pedromoreira.billper I/ViewRootImpl﹕ ViewRoot's Touch Event : Touch UP
07-13 00:50:36.428 26244-26244/net.pedromoreira.billper I/selectItem﹕ Servico: Luz
07-13 00:50:36.428 26244-26244/net.pedromoreira.billper I/onEvent﹕ Servico: Luz
07-13 00:50:36.458 26244-26244/net.pedromoreira.billper E/Event﹕ Could not dispatch event: class net.pedromoreira.billper.events.ServicoActual to subscribing class class net.pedromoreira.billper.MainActivity$ServicoFragment
java.lang.NullPointerException
at net.pedromoreira.billper.MainActivity$ServicoFragment.onEvent(MainActivity.java:235)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at de.greenrobot.event.EventBus.invokeSubscriber(EventBus.java:569)
at de.greenrobot.event.EventBus.postToSubscription(EventBus.java:500)
at de.greenrobot.event.EventBus.postSingleEvent(EventBus.java:475)
at de.greenrobot.event.EventBus.post(EventBus.java:365)
at de.greenrobot.event.EventBus.postSticky(EventBus.java:406)
at net.pedromoreira.billper.MainActivity.selectItem(MainActivity.java:161)
at net.pedromoreira.billper.MainActivity.access$300(MainActivity.java:35)
at net.pedromoreira.billper.MainActivity$DrawerItemClickListener.onItemClick(MainActivity.java:146)
at android.widget.AdapterView.performItemClick(AdapterView.java:299)
at android.widget.AbsListView.performItemClick(AbsListView.java:1158)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:2957)
at android.widget.AbsListView$3.run(AbsListView.java:3849)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5105)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
at dalvik.system.NativeStart.main(Native Method)
07-13 00:50:36.458 26244-26244/net.pedromoreira.billper I/onEvent﹕ Servico: Luz
07-13 00:50:36.468 26244-26244/net.pedromoreira.billper D/Event﹕ No subscribers registered for event class de.greenrobot.event.SubscriberExceptionEvent
07-13 00:50:36.468 26244-26244/net.pedromoreira.billper I/DrawerItemClickListener﹕ Position: 0
07-13 00:50:36.498 26244-26244/net.pedromoreira.billper I/onEvent﹕ Servico: Luz
07-13 00:50:36.508 26244-26244/net.pedromoreira.billper E/Event﹕ Could not dispatch event: class net.pedromoreira.billper.events.ServicoActual to subscribing class class net.pedromoreira.billper.MainActivity$ServicoFragment
java.lang.NullPointerException
at net.pedromoreira.billper.MainActivity$ServicoFragment.onEvent(MainActivity.java:236)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at de.greenrobot.event.EventBus.invokeSubscriber(EventBus.java:569)
at de.greenrobot.event.EventBus.postToSubscription(EventBus.java:500)
at de.greenrobot.event.EventBus.subscribe(EventBus.java:288)
at de.greenrobot.event.EventBus.register(EventBus.java:189)
at de.greenrobot.event.EventBus.registerSticky(EventBus.java:166)
at net.pedromoreira.billper.MainActivity$ServicoFragment.onCreate(MainActivity.java:209)
at android.app.Fragment.performCreate(Fragment.java:1688)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:860)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1063)
at android.app.BackStackRecord.run(BackStackRecord.java:684)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1450)
at android.app.FragmentManagerImpl$1.run(FragmentManager.java:444)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5105)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
at dalvik.system.NativeStart.main(Native Method)
07-13 00:50:36.508 26244-26244/net.pedromoreira.billper D/Event﹕ No subscribers registered for event class de.greenrobot.event.SubscriberExceptionEvent
07-13 00:50:36.508 26244-26244/net.pedromoreira.billper I/onCreateView﹕ mTestText: android.widget.TextView{429216d0 V.ED.... ......ID 0,0-0,0 #7f090003 app:id/test_text}
What can I be doing wrong?
I was wrong. The warning is about nobody being registered at the time of posting. The actual exception probably has to do with the Activity not being attached when the event runs. You probably want onAttach().
http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)
Still feels like kind of a weird architecture, but it would be false to say none of my apps have weird architectures.
Update:
Let me start over. My original answer said EventBus was crashing because nobody was registered for your event. However, that log statement isn't a crash. It's just a debug. I have found EventBus to be a bit aggressive with that kind of thing, but on the balance it probably helps.
The NullPointerException is different. When onCreate() is called on your fragment, it registers to your sticky event. This IMMEDIATELY calls your event. It's right there in the stack. At that point, your fragment is probably attached to your activity, but onCreateView() has (probably) not been called, so your TextView doesn't exist. I can't see which line is throwing the exception exactly. See the life cycle:
If you want to use a sticky event, you'll need to register later in the Fragment life cycle. I would guess you want onCreateView()/onDestroyView(). I've never seen that kind of register/unregister pairing, but it should work.
Generally I think we'd do this with arguments to the fragment, or possibly getting the data from the activity directly, but all methods are fairly clumsy and/or verbose, which is why I'm still an "only when needed" kind of person with the fragments.
An alternative would be to create a Handler in the UI thread and post a Runnable that does the register, which should schedule that after the pending life cycle events, but if you don't need that, don't do it.
Also, you could probably just post/register the Servico rather than the ServicoActual, but that shouldn't make any difference to your issue.

LinearLayout#422725b0 is not a sliding drawer

Hi I have been going crazy with this.
There are several questions out there but none have seems to fix my problem.
I am getting an error when ever I try to style a drawer layout.
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- As the main content view, the view below consumes the entire
space available using match_parent in both dimensions. -->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="#+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:gravity="start"
>
<TextView
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="test"
android:background="#drawable/black"
android:textColor="#94A1A1"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:layout_weight="1"
/>
<ListView
android:id="#+id/left_drawer_child"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:background="#323232"
android:layout_weight="1"
/>
</LinearLayout>
</android.support.v4.widget.DrawerLayout>
and the Java
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.drawertest;
import java.util.Locale;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.SearchManager;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
/**
* This example illustrates a common usage of the DrawerLayout widget
* in the Android support library.
* <p/>
* <p>When a navigation (left) drawer is present, the host activity should detect presses of
* the action bar's Up affordance as a signal to open and close the navigation drawer. The
* ActionBarDrawerToggle facilitates this behavior.
* Items within the drawer should fall into one of two categories:</p>
* <p/>
* <ul>
* <li><strong>View switches</strong>. A view switch follows the same basic policies as
* list or tab navigation in that a view switch does not create navigation history.
* This pattern should only be used at the root activity of a task, leaving some form
* of Up navigation active for activities further down the navigation hierarchy.</li>
* <li><strong>Selective Up</strong>. The drawer allows the user to choose an alternate
* parent for Up navigation. This allows a user to jump across an app's navigation
* hierarchy at will. The application should treat this as it treats Up navigation from
* a different task, replacing the current task stack using TaskStackBuilder or similar.
* This is the only form of navigation drawer that should be used outside of the root
* activity of a task.</li>
* </ul>
* <p/>
* <p>Right side drawers should be used for actions, not navigation. This follows the pattern
* established by the Action Bar that navigation should be to the left and actions to the right.
* An action should be an operation performed on the current contents of the window,
* for example enabling or disabling a data overlay on top of the current content.</p>
*/
public class MainActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;
private LinearLayout linearLayout;
private CharSequence mDrawerTitle;
private CharSequence mTitle;
private String[] mPlanetTitles;
public TextView title;
public int theme = 1;
public int theme2 = 1;
public Drawable background = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(theme == 1){
setTheme(R.style.CustomActionBarTheme);
} else {
setTheme(R.style.CustomActionBarBlueTheme);
}
setContentView(R.layout.activity_main);
mTitle = mDrawerTitle = getTitle();
mPlanetTitles = getResources().getStringArray(R.array.planets_array);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer_child);
linearLayout = (LinearLayout)findViewById(R.id.left_drawer);
// set a custom shadow that overlays the main content when the drawer opens
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
// set up the drawer's list view with items and click listener
mDrawerList.setAdapter(new ArrayAdapter<String>(this,
R.layout.drawer_list_item, mPlanetTitles));
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
// enable ActionBar app icon to behave as action to toggle nav drawer
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);
// ActionBarDrawerToggle ties together the the proper interactions
// between the sliding drawer and the action bar app icon
mDrawerToggle = new ActionBarDrawerToggle(
this, /* host Activity */
mDrawerLayout, /* DrawerLayout object */
R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
R.string.drawer_open, /* "open drawer" description for accessibility */
R.string.drawer_close /* "close drawer" description for accessibility */
) {
public void onDrawerClosed(View view) {
getActionBar().setTitle(mTitle);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
getActionBar().setBackgroundDrawable(background);
}
public void onDrawerOpened(View drawerView) {
// getActionBar().setTitle(mDrawerTitle);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}
};
mDrawerLayout.setDrawerListener(mDrawerToggle);
if (savedInstanceState == null) {
selectItem(0);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}
/* Called whenever we call invalidateOptionsMenu() */
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// If the nav drawer is open, hide action items related to the content view
boolean drawerOpen = mDrawerLayout.isDrawerOpen(linearLayout);
menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
return super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// The action bar home/up action should open or close the drawer.
// ActionBarDrawerToggle will take care of this.
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
// Handle action buttons
switch(item.getItemId()) {
case R.id.action_websearch:
// create intent to perform web search for this planet
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
intent.putExtra(SearchManager.QUERY, getActionBar().getTitle());
// catch event that there's no activity to handle intent
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/* The click listner for ListView in the navigation drawer */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
updatebackground(view,position);
selectItem(position);
}
}
private void updatebackground(View view,int position) {
// update the main content by replacing fragments
int right = view.getPaddingRight();
int left = view.getPaddingLeft();
int top = view.getPaddingTop();
int bottom = view.getPaddingBottom();
switch(position) {
case 0 :
view.setBackgroundResource(R.drawable.redbackground);
background = getResources().getDrawable(R.drawable.red);
break;
case 1 : view.setBackgroundResource(R.drawable.bluebackground);
background = getResources().getDrawable(R.drawable.blue);
break;
case 2 : view.setBackgroundResource(R.drawable.brownbackground);
background = getResources().getDrawable(R.drawable.brown);
break;
case 3 : view.setBackgroundResource(R.drawable.greenbackground);
background = getResources().getDrawable(R.drawable.green);
break;
case 4 : view.setBackgroundResource(R.drawable.orangebackground);
background = getResources().getDrawable(R.drawable.orange);
break;
case 5 : view.setBackgroundResource(R.drawable.purplebackground);
background = getResources().getDrawable(R.drawable.purple);
break;
case 6 : view.setBackgroundResource(R.drawable.pinkbackground);
background = getResources().getDrawable(R.drawable.pink);
break;
case 7 : view.setBackgroundResource(R.drawable.yellowbackground);
background = getResources().getDrawable(R.drawable.yellow);
break;
default: view.setBackgroundResource(R.drawable.blackbackground);
background = getResources().getDrawable(R.drawable.black);
}
view.setPadding(left, top, right, bottom);
getActionBar().setBackgroundDrawable(background);
}
private void selectItem(int position) {
// update the main content by replacing fragments
Fragment fragment = new PlanetFragment();
Bundle args = new Bundle();
args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
fragment.setArguments(args);
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
setTitle(mPlanetTitles[position]);
mDrawerLayout.closeDrawer(linearLayout);
}
#Override
public void setTitle(CharSequence title) {
mTitle = title;
getActionBar().setTitle(mTitle);
if(theme == 1){
theme =2;
}else {
theme =1;
}
}
/**
* When using the ActionBarDrawerToggle, you must call it during
* onPostCreate() and onConfigurationChanged()...
*/
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggls
mDrawerToggle.onConfigurationChanged(newConfig);
}
/**
* Fragment that appears in the "content_frame", shows a planet
*/
public static class PlanetFragment extends Fragment {
public static final String ARG_PLANET_NUMBER = "planet_number";
public PlanetFragment() {
// Empty constructor required for fragment subclasses
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_planet, container, false);
int i = getArguments().getInt(ARG_PLANET_NUMBER);
String planet = getResources().getStringArray(R.array.planets_array)[i];
int imageId = getResources().getIdentifier(planet.toLowerCase(Locale.getDefault()),
"drawable", getActivity().getPackageName());
((ImageView) rootView.findViewById(R.id.image)).setImageResource(imageId);
getActivity().setTitle(planet);
return rootView;
}
}
}
The error I am getting is
11-27 00:56:56.545: E/AndroidRuntime(10000): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.drawertest/com.example.drawertest.MainActivity}: java.lang.IllegalArgumentException: View android.widget.LinearLayout#422725b0 is not a sliding drawer
Any help would be great thanks in advance
The view must have either Gravity.LEFT or Gravity.RIGHT to be recognized as a sliding drawer. You can replace
android:gravity="start"
with
android:gravity="left"
or, as suggested above alternatively replace
isDrawerOpen(linearLayout)
with
isDrawerOpen(Gravity.LEFT)
I just added
android:layout_gravity="start"
and that worked for me.
I also had such an issue. After I added
android:layout_gravity="left"
everything worked out fine.

Modifying Sherlock Master Detail Flow

if you've used the sherlock master detail flow, please help me.
I have added tabs and have removed the data inside the detail fragment/activity but when I try to inflate a button inside the tabs, it doesn't work.
Can you help me?
Here's the list activity that I've modified to display tabs when in the two-pane mode.
package com.example.sample;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.SherlockFragmentActivity;
/**
* An activity representing a list of Courses. This activity has different
* presentations for handset and tablet-size devices. On handsets, the activity
* presents a list of items, which when touched, lead to a
* {#link CourseDetailActivity} representing item details. On tablets, the
* activity presents the list of items and item details side-by-side using two
* vertical panes.
* <p>
* The activity makes heavy use of fragments. The list of items is a
* {#link CourseListFragment} and the item details (if present) is a
* {#link CourseDetailFragment}.
* <p>
* This activity also implements the required
* {#link CourseListFragment.Callbacks} interface to listen for item selections.
*/
public class CourseListActivity extends SherlockFragmentActivity implements
CourseListFragment.Callbacks {
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
*/
private boolean mTwoPane;
private boolean once = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_course_list);
if (findViewById(R.id.course_detail_container) != null) {
// The detail container view will be present only in the
// large-screen layouts (res/values-large and
// res/values-sw600dp). If this view is present, then the
// activity should be in two-pane mode.
mTwoPane = true;
// In two-pane mode, list items should be given the
// 'activated' state when touched.
((CourseListFragment) getSupportFragmentManager().findFragmentById(
R.id.course_list)).setActivateOnItemClick(true);
}
// TODO: If exposing deep links into your app, handle intents here.
}
/**
* Callback method from {#link CourseListFragment.Callbacks} indicating that
* the item with the given ID was selected.
*/
#Override
public void onItemSelected(String id) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
CourseDetailFragment fragment = new CourseDetailFragment();
getSupportFragmentManager().beginTransaction()
.replace(R.id.course_detail_container, fragment).commit();
if (once) {
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// initiating both tabs and set text to it.
ActionBar.Tab assignTab = actionBar.newTab().setText("Assignments");
ActionBar.Tab schedTab = actionBar.newTab().setText("Schedule");
ActionBar.Tab contactTab = actionBar.newTab().setText("Contact");
// Create three fragments to display content
Fragment assignFragment = new Assignments();
Fragment schedFragment = new Schedule();
Fragment contactFragment = new Contact();
assignTab.setTabListener(new MyTabsListener(assignFragment));
schedTab.setTabListener(new MyTabsListener(schedFragment));
contactTab.setTabListener(new MyTabsListener(contactFragment));
actionBar.addTab(assignTab);
actionBar.addTab(schedTab);
actionBar.addTab(contactTab);
once = false;
}
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, CourseDetailActivity.class);
startActivity(detailIntent);
}
}
class MyTabsListener implements ActionBar.TabListener {
public Fragment fragment;
public MyTabsListener(Fragment fragment) {
this.fragment = fragment;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.replace(R.id.twopanecontainer, fragment);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.remove(fragment);
}
}
}
Here's the fragment course detail layout that holds a textview by default.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/twopanecontainer"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/course_detail"
style="?android:attr/textAppearanceLarge"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".CourseDetailFragment" />
</LinearLayout>
What should I modify so I can inflate a different view for each tab?
Thanks
Have a ViewPager in yout activity layout, then implement FragmentPagerAdapter

Categories

Resources