Correctly creating a dpad-compatible UI - android

I want to develop an application that can be 100% functional even using a dpad, but not for TV devices, so I don't want to rely on Leanback library. I have already followed the guidelines here in order to focus the proper elements of my UI.
I am using the support design library and I'm having several issues with my NavigationView:
When I open the drawer for the first time, it has the first item checked, but another item focused
If I close and open the drawer again, there is no item checked and focus is lost
This is what I'm doing to handle the focus:
private void initDrawer(){
drawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
navigationView = (NavigationView)findViewById(R.id.navigation_view);
navigationView.setNavigationItemSelectedListener(this);
drawerToggle = new ActionBarDrawerToggle(MainActivity.this, drawerLayout, R.string.app_name, R.string.app_name){
#Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
//This is not working properly
navigationView.requestFocus();
}
#Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
//This seems to work
mContentLayout.requestFocus();
}
};
drawerLayout.setDrawerListener(drawerToggle);
}
This is what I'm doing to toggle the drawer opening/closing using an hardware button:
#Override
public boolean onKeyUp(int keyCode, KeyEvent event){
if(keyCode==KeyEvent.KEYCODE_BUTTON_Y){
drawerToggle.syncState();
if(drawerLayout.isDrawerOpen(GravityCompat.START)){
drawerLayout.closeDrawer(GravityCompat.START);
}
else {
drawerLayout.openDrawer(GravityCompat.START);
}
return true;
}
else return super.onKeyUp(keyCode, event);
}
I would like to focus the first item on the NavigationView everytime I open the drawer. What am I missing?

This is a known bug of design support library, apparently.
Google claims it's been fixed in 23.1.1 but it doesn't work.
I had to make these changes in my onDrawerOpened method:
#Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
if(navigationView.requestFocus()){
NavigationMenuView navigationMenuView = (NavigationMenuView)navigationView.getFocusedChild();
navigationMenuView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
}
}

Related

Android Drawer with button to open it that moves with the drawer

The question says it all. I know how to implement a drawer with the drawerlayout, but i have no idea how to place a button next to the drawer (to show the drawer) that moves along with the drawer.
You have to use ActionBarDrawerToggle.
ActionBarDrawerToggle myDrawerToggle = new ActionBarDrawerToggle(this, myDrawerLayout, R.drawable.my_icon_for_drawer_toggle,
R.string.drawer_open, R.string.drawer_close) {
#Override
public void onDrawerClosed(View drawerView) {
invalidateOptionsMenu();
}
#Override
public void onDrawerOpened(View drawerView) {
invalidateOptionsMenu();
}
};
myDrawerLayout.setDrawerListener(myDrawerToggle);
I think what you want is sliding menu.
Look at this Github project which also provides example code for it
https://github.com/jfeinstein10/SlidingMenu
Hope this helped!

Drawer Indicator does not show

I can't have the Drawer Indicator display. Currently I have either nothing or the "<" at the top left corner of the screen depending of the actionBar settings. But I want the Drawer Indicator of the Nagivation Drawer instead.
I use :
v4.widget.DrawerLayout
v7.app.ActionBarDrawerToggle
but android.app.ActionBar (not the support 7 one).
Here is snippet of the code :
#Override
protected void onCreate(Bundle savedInstanceState)
{
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
//I tried all combinations unsuccessfully
....
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawerLV = (ListView) findViewById(R.id.left_drawer);
drawer_Linearlayout = (LinearLayout) findViewById(R.id.drawer_Linearlayout);
drawerLV.setAdapter(new ArrayAdapter<String>(
this,
R.layout.layout_main_drawer_list_item,
mDrawerItems));
drawerLV.setOnItemClickListener(new ListView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
});
drawerToggle = new ActionBarDrawerToggle(this, drawer, R.string.drawer_open,R.string.drawer_close) {
#Override
public void onDrawerClosed(View view) {
actionBar.setTitle(mTitle);
}
#Override
public void onDrawerOpened(View drawerView) {
actionBar.setTitle(mDrawerTitle);
}
};
drawerToggle.setDrawerIndicatorEnabled(true);
drawer.setDrawerListener(drawerToggle);
}
I eventually fixed my problem.
I forgot to add the following callback in my Acticity :
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
drawerToggle.syncState();
}
By the way, Lollipop upgrade of v7.app.ActionBarDrawerToggle adds a nice effect when the navigation drawer is opening or closing. I recommend it.
I think you also must specify android.R.id.home it in the onOptionsItemSelected to make the back button visible :
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
//the onClick for your back button
return true;
default:
return super.onOptionsItemSelected(item);
}
}
And you just need to use actionBar.setDisplayHomeAsUpEnabled(true);
UPDATE :
Just did a quick googling, take a look at this :
Display back button on action bar
Try my answer. I think it will solve your problem.
Sorry for my previous answer, I misread it. Think this is what you need:
mDrawerToggle = new ActionBarDrawerToggle(
this,
mDrawerLayout,
R.drawable.ic_drawer, /* The image drawable you're missing */
R.string.navigation_drawer_open,
R.string.navigation_drawer_close
The image is custom, also known as the hamburger.

Android - Delay expand of SearchView

I want to delay the expand of SearchView. I use a drawerlayout. If the search item is clicked when drawer open, then layout of my views become distorted (a disaster), so I want to close drawer first before expanding search View. Any idea how to achieve this?
I use the following code. I tried to intercept menuitem selected event, triggering drawer to close. And in DrawerListener, call the serchMenu.expandActionView();
I expect the searchView to expand after drawer completed closed, but these two actions always happen simultaneously, and during the transition, the layout of my views become extremely distorted as well. That why I what delay the expand of search event.
public boolean onOptionsItemSelected(MenuItem item) {
SearchView searchView = (SearchView) mSearchItem.getActionView();
if(item.getItemId() == R.id.search){
mDarwerLayout.closeDrawer(mLeftDrawer);
mSearchViewShouldExpand = true;
}
return true;
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawer_layout);
.......
mDrawerToggle = new ActionBarDrawerToggle(
this, /* host Activity */
mDarwerLayout, /* DrawerLayout object */
R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */
0,
0
){
/** Called when a drawer has settled in a completely closed state. */
#Override
public void onDrawerClosed(View drawerView) {
if(mSearchViewShouldExpand){
mSearchItem.expandActionView();
mSearchViewShouldExpand = false;
}
}
};
// Set the drawer toggle as the DrawerListener
mDarwerLayout.setDrawerListener(mDrawerToggle);
}
I found the way to intercept expand event, it is in MenuItem.OnActionExpandListener. Just need to return false in this method
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
Log.d("*******","onMenuItemActionExpand");
mDarwerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
if(mDarwerLayout.isDrawerOpen(mRightDrawer) || mDarwerLayout.isDrawerOpen(mLeftDrawer) ) {
mDarwerLayout.closeDrawer(mRightDrawer);
mDarwerLayout.closeDrawer(mLeftDrawer);
mSearchViewShouldExpand = true;
return false;
}
return true;
}

How to open navigation drawer on button click in main fragment?

I have made an app with one activity which uses a navigation drawer to open a number of different fragments. I have the actionbar drawertoggle, but it is not very visible.
If I place a button in the onCreateView in my main fragment(the fragment that appears when my app first starts up), how can I get it to open the navigation drawer controlled by my activity?
This seems to work. The answer is much simpler than I thought it would be.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View fragView = inflater.inflate(R.layout.mainmenu, container, false);
button1 = (Button) fragView.findViewById(R.id.button1);
mDrawerLayout = (DrawerLayout)getActivity().findViewById(R.id.drawer_layout);
mDrawerList = (ListView)getActivity().findViewById(R.id.left_drawer);
button1.setOnClickListener(this);
return fragView;
}
#Override
public void onClick(View v) {
mDrawerLayout.openDrawer(mDrawerList);
}
Thank you for your answers.
if you need open the slide:
mDrawerLayout.openDrawer(Gravity.LEFT); //Edit Gravity.START need API 14
if you need close the slide
mDrawerLayout.closeDrawer(Gravity.LEFT); //Edit Gravity.START need API 14
EXAMPLE
my mDrawerLayout is instanced here:
mDrawerLayout = (DrawerLayout)findViewById(R.id.my_drawer_layout);
my slide state:
mSlideState=false;
if you need to know the slide menu state (closed, opened). Use this code:
mDrawerLayout.setDrawerListener(new ActionBarDrawerToggle(this,
mDrawerLayout,
R.drawable.ic_menu_slide,
0,
0){
#Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
mSlideState=false;//is Closed
}
#Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
mSlideState=true;//is Opened
}});
finally. You can use your click event like this:
public void clickEventSlide(){
if(mSlideState){
mDrawerLayout.closeDrawer(Gravity.END);
}else{
mDrawerLayout.openDrawer(Gravity.END);
}}
In my case, my slide menu is at the right (Gravity.END), but if you need it on the left, try with Gravity.START
You Should Use isDrawerOpen()
The piece of code below automatically closes or opens the navigation drawer based on the drawer's current state (Opened or Closed)
Button hamMenu = findViewById(R.id.ham_menu);
hamMenu.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DrawerLayout navDrawer = findViewById(R.id.drawer_layout);
// If the navigation drawer is not open then open it, if its already open then close it.
if(!navDrawer.isDrawerOpen(Gravity.START)) navDrawer.openDrawer(Gravity.START);
else navDrawer.closeDrawer(Gravity.END);
}
});
if you are using from default navigation activity in android you just have to
add this code in click listener of button --->
mDrawerLayout.openDrawer(Gravity.START);
for closing you do not have to do something.
Use these lines to open and close the drawer on a certain event:
Code snippet for opening drawer:
drawerLayout.openDrawer(Gravity.START);
Code snippet for closing drawer:
drawerLayout.closeDrawer(Gravity.LEFT);
→ openDrawer(gravity_of_navigation_view_to_be_shown)
in openDrawer("gravity"), in "gravity" section, you have to input the gravity of the Navigation View like given above:
Gravity.LEFT
Gravity.RIGHT
Gravity.START
Gravity.END
I think thats the best answer.
To apply the toolbar as the app bar, first make sure your activity extends from AppCompatActivity. Then call setSupportActionBar() and pass the Toolbar object from your layout:
toolbar=(Toolbar) findViewById(R.id.toolbar_main);
setSupportActionBar(toolbar);
ActionBar actionbar = getSupportActionBar();
actionbar.setDisplayHomeAsUpEnabled(true);
actionbar.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp);
drawerLayout=(DrawerLayout)findViewById(R.id.drawer_layout);
ActionBarDrawerToggle actionBarDrawerToggle=new ActionBarDrawerToggle(this,drawerLayout,R.string.navigation_drawer_open,
R.string.navigation_drawer_close);
drawerLayout.addDrawerListener(actionBarDrawerToggle);
actionBarDrawerToggle.syncState();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
drawerLayout.openDrawer(GravityCompat.START);
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)){
drawerLayout.closeDrawer(GravityCompat.START);
}
else{
super.onBackPressed();
}
}
}
The simplest way in my opinion
#Override
public void onBackPressed() {
if(mDrawerLayout.isDrawerOpen(findViewById(R.id.navigationViewId))){
mDrawerLayout.closeDrawer(Gravity.LEFT);
}else
super.onBackPressed();
}

Navigation drawer - Disable click through on items behind the drawer

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();
}

Categories

Resources