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
Related
i want to display a list or some kind of menu in navigation drawer at runtime. I have one single activity in which i put fragment using frame layout. This fragment contains Webview. Whenever user clicks on navigation item, then webview opens its alternative link. I successfully received response from Retrofit 2.3, but now i dont know how to achieve this
public void onResponse(Call<List<NavBarResult>> call, Response<List<NavBarResult>> response) {
List<Result> result = response.body();
//what to do here ?
}
#Override
public void onFailure(Call<List<NavBarResult>> call, Throwable t) {
//error messages here
}
My JSON API look like:
[
{
menulist_id: "1",
mname: "Item 1",
mlink: "http://example.com/technology/",
},
{
menulist_id: "2",
mname: "Item 2",
mlink: "http://example.com/sports/",
}
]
I want to work like that all menu's items work automatically even if we change the data from API. Thanks in advance.
Possible duplicate.
Android - Add bottom navigation view dynamically
How can I add a menu dynamically to bottom navigation view?
You can dynamically get the menu from your navigation view, then add a new menu item with you JSON response.
In your OnNavigationItemSelectedListener you can change the URL of your webview.
EDIT 1
Maybe I misunderstood something. Do you want to populate a Navigation Bar with your JSON response ? In that case, my links above can help you.
Do you want to populate a Navigation Drawer with your JSON response ?
In that case you should begin read this : https://developer.android.com/training/implementing-navigation/nav-drawer.html
Edit your activity 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">
<!-- The main content view -->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- The navigation drawer -->
<ListView android:id="#+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:background="#111"/>
</android.support.v4.widget.DrawerLayout>
content_frame will contain your fragment with a WebView.
left_drawer will contain your dynamics links.
Get the left_drawer :
mDrawerList = (ListView) findViewById(R.id.left_drawer);
When you get your JSON response just set a adapter to your mDrawerList :
public void onResponse(Call<List<NavBarResult>> call, Response<List<NavBarResult>> response) {
// result is a private List<Result> attribute of the class.
result = response.body();
// Set the adapter for the list view
mDrawerList.setAdapter(new ArrayAdapter<Result>(this,
android.R.layout.simple_list_item_1, result));
// Set the list's click listener
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
}
This way, your Drawer Layout will be populated with your JSON response dynamically.
Then you just have to define your OnItemClickListener to call a new fragment when you click on an item of your list.
private class DrawerItemClickListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
}
MyFragment is not developp here but it would contain a webView and wait for a ARG_LINK that should be an URL to display in its webView.
private void selectItem(int position) {
// Create a new fragment and specify the link to show based on position
Fragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putString(MyFragment.ARG_LINK, result.get(position).getUrl());
fragment.setArguments(args);
// Insert the fragment by replacing any existing fragment
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
// Highlight the selected item and close the drawer
mDrawerList.setItemChecked(position, true);
mDrawerLayout.closeDrawer(mDrawerList);
}
Hope it help you this time !
You can add a navigation view in your navigation drawer then when you are receiving the items from the API you can add Items to the menu see below code to add the items from the menu.
final Menu menu = navigationView.getMenu();
//Here you should add the length of your json array instead 'i' .
for (int i = 1; i <= 3; i++) {
menu.add("Runtime item "+ i);
}
The navigation drawer code is as follows:
private void ShowNavigationDrawer() {
// DrawerLayout
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
// Populate the Navigtion Drawer with options
mDrawerList = (ListView) findViewById(R.id.left_drawer);
DrawerListAdapter adapter = new DrawerListAdapter(this, mNavItems);
mDrawerList.setAdapter(adapter);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
R.drawable.ic_drawer_white, R.string.drawer_open,
R.string.drawer_close);
// Drawer Item click listeners
mDrawerList
.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
selectItemFromDrawer(position);
}
});
}
The other used methods are:
/*
* Called when a particular item from the navigation drawer is selected.
*/
private void selectItemFromDrawer(int position) {
selectItem(position);
getSupportActionBar().setTitle(mNavItems.get(position).mTitle);
// Close the drawer
mDrawerLayout.closeDrawer(mDrawerList);
}
To select the correct fragment to load within one activity:
/** Swaps fragments in the main content view */
private void selectItem(int position) {
switch (position) {
case 1:
currentFragment = new ABCFragment();
break;
case 2:
currentFragment = new SearchTabFragment();
break;
default:
currentFragment = new HomeFragment();
break;
}
currentFragment.setArguments(getIntent().getExtras());
fragmentManager = getSupportFragmentManager();
android.support.v4.app.FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
fragmentTransaction.replace(R.id.main_content_frame, currentFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
// Highlight the selected item, update the title, and close the drawer
mDrawerList.setItemChecked(position, true);
}
To select(highlight) the particular item from the navigation drawer programatically within another fragment I'm calling the following method but it is NOT working!
// Highlight the selected item
homeActivity.mDrawerList.setItemChecked(position, true);
Where is the problem? Can anyone help me to fix this issue?
You can try to implement the navigation drawer with the new NavigationView instead of the ListView. Something like this:
<android.support.design.widget.NavigationView
android:id="#+id/nv_navigation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:itemIconTint="#color/navigation_item_selector"
app:itemTextColor="#color/navigation_item_selector"
app:itemBackground="#android:drawable/screen_background_light_transparent"
android:layout_gravity="start" />
You can populate the NavigationView with a menu .xml file. Then you can easily select programmatically an item like this:
mNavigationView.setCheckedItem(R.id.my_item_1);
and the drawable selector will handle the work of the highlighting in the correct manner. For example, this is my selector (navigation_item_selector.xml):
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#color/red" android:state_checked="true" />
<item android:color="#color/red" android:state_pressed="true" />
<item android:color="#color/black" />
</selector>
PS I know its not the solution that you want, but its the solution that you might find helpful.
Since highlighting means giving it a background color as you want. If you do not want it in the click event of items but want it otherwise, you have to give it manually on your navigation drawer adapter. like this
public void onBindViewHolder(MyViewHolder holder, int position) {
NavDrawerItem current = data.get(position);
holder.title.setText(current.getTitle());
if(position==0)
{
holder.llnavdrawer.setBackgroundResource(R.color.black);
}
else if(position==2)
{
holder.llnavdrawer.setBackgroundResource(R.color.grey);
}
else if(position==4)
{
holder.llnavdrawer.setBackgroundResource(R.color.green);
}
else if(position==6)
{
holder.llnavdrawer.setBackgroundResource(R.color.black);
}
}
you can ask if the thing you wanted is not this.
Good luck!!
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
I am currently re-coding most of the back end of my android app in order to follow the design guidelines more closely. Currently I am using all activities and zero fragments. I am trying to switch to fragments in order to use the slide out navigation draw and eventually some sliding tabs.
For navigation right now I have this drop down menu which when an item is clicked launches a new activity:
The "Your Statistics" activity is kind of like the home page, where the user will enter the app too. I also want the user to be able to get back to that "page" from anywhere in the app.
My activity that I plan to run the draw from I have a draw layout called fragment_main:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<FrameLayout
android:id="#+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
<ListView
android:id="#+id/drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#FFF"
android:choiceMode="singleChoice"/>
</android.support.v4.widget.DrawerLayout>
and my activity which loads the drawer layout is:
public class MainDraw extends FragmentActivity {
final String[] data ={"one","two","three"};
final String[] fragments ={
"com.beerportfolio.beerportfoliopro.FragmentOne",
"com.beerportfolio.beerportfoliopro.FragmentTwo",
"com.beerportfolio.beerportfoliopro.FragmentThree"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
//todo: load statistics
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActionBar().getThemedContext(), android.R.layout.simple_list_item_1, data);
final DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
final ListView navList = (ListView) findViewById(R.id.drawer);
navList.setAdapter(adapter);
navList.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, final int pos,long id){
drawer.setDrawerListener( new DrawerLayout.SimpleDrawerListener(){
#Override
public void onDrawerClosed(View drawerView){
super.onDrawerClosed(drawerView);
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main, Fragment.instantiate(MainDraw.this, fragments[pos]));
tx.commit();
}
});
drawer.closeDrawer(navList);
}
});
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main,Fragment.instantiate(MainDraw.this, fragments[0]));
tx.commit();
}
}
IN my //todo: comment I should load my first "home" fragment there which is my statistics "page" ? And then all the other fragments will be transitioned in and out based on the draw clicks?
Thanks for your help in advance, I want to make sure I am doing this right, I used to code just to get things working which is why I am now re doing a huge chunk of my code. Please share any other fragment tips to that I might need!
First of all read the well written documentation, it answers to your doubts.
I would share my personal pattern to convert existing Activity to Fragment
Create your on abstract Fragment class from which derive all drawer fragments, this can help to group common attributes
Use a method like selectItem() on docs, it helps to explicit do a call at first run (showing the "home" fragment) and then from onItemClick
move inflating XML layout from Activity.onCreate() code to Fragment.onCreateView() (ie setContentView to inflater.inflate(R.layout.my_layout, container, false), in many cases you can copy all code from onCreate() to onCreateView
move initialization code from Activity.onCreate() to Fragment.onActivityCreated(), this is very useful when both Activity (including fragment) and the direct Fragment exist, for example if your app exposes a "Share with" action you continue to have the Activity that inside the XML includes a <fragment/> and the fragment can be created from the drawer, too
if you need to communicate from Activity to Fragment and viceversa I suggest to create an interface and store it inside the 'onAttach()' (see google example)
Action bar items must be hidden when drawer is open, again take a look at example used in doc, here is very useful the interface to communicate from activity to fragment, the main activity can tell if drawer is open and the fragment can call the interface
public interface FragmentActivityStatus {
public boolean isDrawerOpen();
}
The activity
public class MainActivity extends Activity implements FragmentActivityStatus {
#Override
public boolean isDrawerOpen() {
return drawerLayout.isDrawerOpen(drawerList);
}
}
The fragment
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
fragmentActivityStatus = (FragmentActivityStatus)activity;
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
boolean isMenuVisible = !fragmentActivityStatus.isDrawerOpen();
menu.findItem(R.id.my_menu).setVisible(isMenuVisible);
super.onPrepareOptionsMenu(menu);
}
Not related to fragment, in your code you declare class names as string, consider to create a Class array if you refactor packages the code continue to work, then you can call the Class.getName() to obtain the string to pass to Fragment.instantiate()
final Class<?>[] fragments = {
FragmentOne.class,
FragmentTwo.class,
FragmentThree.class};
Then
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main, Fragment.instantiate(MainDraw.this,
fragments[pos].getName()));
tx.commit();
Is there any way to make sure that the navigation drawer stays on top of the content in the fragment?
I created a small test application with dummy data. 10 fragments with a corresponding numbered button and textview. The issue is with the fact that the fragment elements seem to have higher priority than the navigation drawer.
As seen in the screenshot, once I attempt to open up the "0 fragment" it instead opts to register the click on the button behind the navigation drawer. Pressing any other content item works fine, but this is as long as there are no other interactable items beneath them. What can I do to have the navigation drawer properly stay on top of the content behind it?
Set android:clickable="true" tag on sliding pane layout.
The problem seem not because of click focus,
Visit https://developer.android.com/training/implementing-navigation/nav-drawer.html#DrawerLayout
The main content view (the FrameLayout above) must be the first child in the DrawerLayout because the XML order implies z-ordering and the drawer must be on top of the content.
In my fragment drawer, I set TouchListener to return True.
It worked for me
mFragmentContainerView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
I solved it in a different way.
Here is my code for setting up the Drawer:
/**
* Setup Navigation Drawer
*/
private void setDrawer() {
NavigationDrawerFragment mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.fragment_drawer);
mNavigationDrawerFragment.setup(R.id.fragment_drawer, (DrawerLayout) findViewById(R.id.drawer), mToolbar);
}
the setup method is inside my NavigationDrawerFragment, here is my code for it:
/**
* Users of this fragment must call this method to set up the navigation drawer interactions.
*
* #param fragmentId The android:id of this fragment in its activity's layout.
* #param drawerLayout The DrawerLayout containing this fragment's UI.
* #param toolbar The Toolbar of the activity.
*/
public void setup(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar) {
View mFragmentContainerView = (View) getActivity().findViewById(fragmentId).getParent();
DrawerLayout mDrawerLayout = drawerLayout;
//noinspection deprecation
mDrawerLayout.setStatusBarBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
ActionBarDrawerToggle mActionBarDrawerToggle = new ActionBarDrawerToggle(getActivity(), mDrawerLayout, toolbar, "Drawer opened", "Drawer closed") {
#Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
if (!isAdded()) return;
// Solution:
// Disable click event on views below Navigation Drawer
mFragmentContainerView.setClickable(false);
getActivity().invalidateOptionsMenu();
}
#Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
if (!isAdded()) return;
// Solution:
// Enable click event on views below Navigation Drawer
mFragmentContainerView.setClickable(true);
getActivity().invalidateOptionsMenu();
}
};
// Defer code dependent on restoration of previous instance state.
mDrawerLayout.post(new Runnable() {
#Override
public void run() {
mActionBarDrawerToggle.syncState();
}
});
//noinspection deprecation
mDrawerLayout.setDrawerListener(mActionBarDrawerToggle);
}
That's it
You have to set clickable, focusable and focusableInTouchMode in the highest view of your drawer's layout.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true">
Well, I found one solution for this issue. When the drawer is being opened you can bring the nav bar to the front by calling the bringToFront()-method on the layout you use. This makes sure the navigation drawer stays on top of any underlying content until a new item has been selected.
I.e:
#Override
public void onDrawerOpened(View drawerView)
{
activity.getActionBar().setTitle("Select content");
activity.invalidateOptionsMenu();
drawerLayout.bringToFront();
}