I'm using a ListView as a secondary view in my SlidingPaneLayout.The main view is a map fragment. The ListView acts as a menu. The problem is that onItemClickedListener never gets called on the ListView. Even the list row never gets highlighted on press. it seems that the ListView can't get the focus.
EDIT:
actually, slidingPaneLayout.findFocus() shows that android.widget.ListView. still no luck on clicking the list items.
Here is my xml
<com.ziz.luke.custom_components.MySlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/slidingpanelayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="#+id/contactsList"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#00000000" >
</ListView>
<TextView
android:id="#+id/android:empty"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_horizontal|center_vertical"
android:text="#string/noContacts" />
</RelativeLayout>
<fragment
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
</com.ziz.luke.custom_components.MySlidingPaneLayout>
How can I solve this??
I've found the answer. I was using a subclass of SlidingPaneLayout in which I was overriding
onInterceptTouchEvent(MotionEvent arg0)
I was trying to do the following:
open the slidingPaneLayout useing a button.
close the slidingPaneLayout useing a button.
close the slidingPaneLayout useing swiping.
prevent the user from opening the slidingPaneLayout using swiping.
So, I created a boolean inside my subclass called shouldSwipe to be returned from the over-ridden method.
the implementation that caused the problem was :
#Override
public boolean onInterceptTouchEvent(MotionEvent arg0) {
return shouldSwipe;
}
it caused the problem whenever (shouldSwipe = true) because it tells the system that the touch event already is consumed and prevents it from being propagated.
I solved that using this one:
#Override
public boolean onInterceptTouchEvent(MotionEvent arg0) {
// TODO Auto-generated method stub
return shouldSwipe ?super.onInterceptTouchEvent(arg0):shouldSwipe;
}
that's it.
Just a suggestion but maybe using the NavigationDrawer for a Navigation List Drawer would be easier then reinventing the wheel.
http://developer.android.com/design/patterns/navigation-drawer.html
I have just created an example project using SlidingPaneLayout.
I didn't use any map because there is not where the problem is, so I just refer the position of the map with a TextView. I did use a ListFragment that is working and receiving the click listeners. Please download the project from here:
https://dl.dropboxusercontent.com/u/33565803/StackOverFlowExamples/SlidingPaneLayoutExample.zip
Let me know if you have any configuration problem and if it solves your problem ;)
(I am using actionBarSherlock just because I am used to, so you can remove it if you want)
Try using this powerful library to make a simple sliding bar :
https://github.com/jfeinstein10/SlidingMenu
and here is how you can import it and use it :
Solved: how to import SlidingMenu on my project with ActionBarSherlock 4.2.0
Related
I started a project with a Navigation Drawer from the basic template of Android Studio. The only modification I made was to display it as permanent in order to have a tablet/TV layout.
To achieve this, the only modification I made was in the xml layout. This allow the NavigationView to be always visible.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Content will come here" />
</LinearLayout>
</LinearLayout>
I also put the project on Github, so anyone can test it.
PROJECT DEMO ON GITHUB
https://github.com/ChristopheVersieux/NavFocus
WHAT IS HAPPENING
My issue comes when I start selecting items on the drawer with the D-pad.
Once an item is selected, the focus is completely lost. Trying to get back to the Drawer and get focus seems very hard and I have to try several times with right/left arrows
WHAT IS EXPECTED:
Drawer should keep focus, or focus should be easy to bring back to the Drawer.
WHAT I TRIED:
The simplest Idea I had was to force the Drawer to get focus again, but this code doesn't change anything:
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
menuItem.setChecked(true);
//This is where I will replace the Fragments in the right area.
navigationView.clearFocus();
navigationView.requestFocus();
return true;
}
});
Thank a lot for your help.
I would start by removing android:layout_gravity="start"
This is simply not needed as its parent is a horizontal LinearLayout.
The Navigation Drawer must be permanently visible on Tablets and TV. They stay hidden for mobile. These are part of the Material Design guidelines.
This requires quite a different setup compared to what I see in your project on GitHub. Which includes supplying different resources using qualifiers.
This tutorial on Navigation Drawer (Design Support) will help you with exactly that setup, as per the latest Material Design guidelines. Alternatively the project files for the tutorial can be found on GitHub.
UPDATE:
As pointed out, Support library v24 creates issues with the dpad. Reverting to v23 works just fine.
I've been trying to move my code across to the DrawerLayout as suggested by android here as SlidingDrawer is deprecated.
My problem is that so far DrawerLayout seems to be either very badly implemented, has unhelpful error messages (no defensive programming) and/or isn't explained well enough in the documentation.
the isDrawerOpen() method is described here:
public boolean isDrawerOpen (View drawer)
Check if the given drawer view is currently in an open state. To be
considered "open" the drawer must have settled into its fully visible
state. To check for partial visibility use
isDrawerVisible(android.view.View).
Parameters: drawer - Drawer view to check
Returns: true if the given drawer view is in an open state
Each of the methods isDrawerOpen(View drawer), openDrawer(View drawer) and closeDrawer(View drawer) don't work when passed: The DrawerLayout in question or either of it's children. I have no idea what I'm supposed to feed into these methods to allow them to function. Can someone let me know?
See below for entire problem description with implementation.
I have a layout like so:
<android.support.v4.widget.DrawerLayout
android:id="#+id/mainmenuPanel"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/dualPane"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/menuPane"
android:layout_width="300dp"
android:layout_height="match_parent" />
</android.support.v4.widget.DrawerLayout>
And in my code I have the following method hooked up to a button:
protected void onCreate(Bundle savedInstanceState) {
...
mMenuPanel = (DrawerLayout) findViewById(R.id.mainmenuPanel);
....
}
public boolean isDrawerOpen() {
if(mMenuPanel != null) {
return mMenuPanel.isDrawerOpen(mMenuPanel);
}
return false;
}
However if you give it itself as an argument (which would be redundant in the extreme) you get the following error:
E/AndroidRuntime(11241): java.lang.ClassCastException:
android.widget.FrameLayout$LayoutParams cannot be cast to
android.support.v4.widget.DrawerLayout$LayoutParams
(Which as you'll notice, doesn't give you any information about what you did wrong. It is instead a symtom of the problem).
Other answers here or here are either incomprehensible or very closely tied to the question without much explanation. Even so I've tried adding a faux LinearLayout or one the DrawerLayouts children and each give this error:
E/AndroidRuntime(11424): java.lang.IllegalArgumentException:
View android.widget.FrameLayout{420f5ea8 V.E..... ........
0,0-800,1172 #7f060038 app:id/menuPane} is not a drawer
Can anyone explain what these methods actually need to have passed to them to work?
And, the answer:
The second child (aka, the "Drawer") is what needs to be passed to the methods. My problem was that by the time I had figured that out I'd reduced the layout to as simple as possible implementation to test - I'd removed the gravity from the "drawer". Without a gravity, you get the above completely unrelated error messages.
I can confirm I got the code to work using the following setup:
mMenuPanel.isDrawerOpen(findViewById(R.id.drawer));
and with the layout:
<android.support.v4.widget.DrawerLayout
android:id="#+id/mainmenuPanel"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/drawer"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="start"/> <!-- This line was the problem!!!!!!!-->
</android.support.v4.widget.DrawerLayout>
if you want to avoid references to views or layouts in order to call isDrawerOpen, another way can be applied.
In this case you have to indicate the Gravity:
mDrawerLayout.isDrawerOpen(Gravity.START);
Being mDrawerLayout a reference to your DrawerLayout.
as you said, don't forget android:layout_gravity="start"
In your Activity if you have a reference to the DrawerLayout and in this case the FrameLayout with the id R.id.menuPane you can also do...
DrawerLayout mMenuPanel = (DrawerLayout) findViewById(R.id.mainmenuPanel);
FrameLayout mMenuPane = (FrameLayout) findViewById(R.id.menuPane);
mMenuPanel.isDrawerOpen(mMenuPane);
Basically you have to pass in a reference to the view being used as the drawer within the DrawerLayout, which will always be mMenuPanel.getChildAt(1) anyways.
If you are using the drawer layout from the support library as we can see from your question that you are, there is also the method:
closeDrawers()
This will close all open drawers (which is usually just the one).
I am trying to use Chris Banes' library Actionbar-PullToRefresh. It can be found here.
I am using Tabs + ViewPager + Fragments in my app.
The problem I'm facing is that my fragment has a GridView and I cannot figure out how to use this library to work with it.
I read through the sample code. He says that all you have to do is, wrap your refreshable view in a PullToRefreshLayout like this:
<uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ptr_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<!-- Your content, here we're using a ScrollView -->
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</ScrollView>
</uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout>
This works great for stuff like ListView, ScrollView, GridView, etc. However, apparently this will not work for Fragments (Tabs & ViewPagers). Now, in the sample code he has wrapped the refreshable fragment with a ScrollView INSTEAD of a PullToRefreshLayout.
I cannot do this because my Fragment 1 (under tab 1) has a GridView. Now I cannot add a GridView to a ScrollView because that just wouldn't make sense.
For example, if I put my GridView inside the ScrollView as shown below, it just doesn't make sense:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ptr_scrollview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:scrollbarStyle="outsideInset" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#FFF000" >
<!-- MY GRID VIEW -->
<GridView
...
/>
</RelativeLayout>
</ScrollView>
The above code works. Sort of. (I need to disable scrolling of my GridView and use an ExpandableGridView to get it working properly... which seems like overkill & I'm not even sure if that would work).
If I replace the ScrollView wrapper with anything else like PullToRefreshLayout or any other layout, the refreshing doesn't work.
What to do? How to get my layout to work with this library without wrapping a ScrollView around it?
I hope I was clear enough. Please let me know if you need any more info. I tried to explain it the best I could.
Thanks!
I had a similar issue trying to get it to work with a ListView that I had in one of my tabs.
I solved my issue by using the PullToRefreshAttacher instead of using the layout.
In your Activity that is controlling the ViewPager for the fragments, initialize a PullToRefreshAttacher in onCreate or an init method.
mPullToRefreshAttacher = PullToRefreshAttacher.get(this);
Next make a public method that allows access to the attacher that you just initialized.
public PullToRefreshAttacher getPullToRefreshAttacher() {
return mPullToRefreshAttacher;
}
Then in the fragment you want the refresh functionality.
mPullToRefreshAttacher = ((MainTabActivity) getActivity())
.getPullToRefreshAttacher();
mPullToRefreshAttacher.addRefreshableView(activeListView, this);
Except in your case activeListView would be the reference to your GridView instead of a ListView.
Then make sure your fragment implements OnRefreshListener so you can handle the Refresh.
I have not actually tested this with a GridView so let me know if it works.
Good Luck!
I am using a android.support.v4.widget.DrawerLayout in my activity.
As long as I use the swipe gesture to open it, it works fine.
However, when I want to open it through a button click by calling drawer.openDrawer(Gravity.LEFT), it does not work.
But, if I open it once using the swipe gesture, after that it works normally with button click.
Any idea how I could solve or work around this?
I had the same issue and I've just found out that for some reason the FrameLayout that represents the drawer have the visibility set to "gone", that probably goes to "visible" during the first slideGesture.
So, open your layout xml file, locate your FrameLayout that represents the drawer and simply erase the visibility setting. My opening tag is now as follows:
<FrameLayout
android:layout_width="305dp"
android:layout_height="match_parent"
android:layout_gravity="start">
That worked for me.
Cheers
In my case the visibility on 'NavigationView' was set to gone in the layout. Changing it to visible solved the issue
If you want to open it from the Top Left Toggle you should implement onOptionsItemSelected(MenuItem item)
#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;
}
switch (item.getItemId()) {
case android.R.id.home:
return true;
}
return super.onOptionsItemSelected(item);
}
Please use clearFocus() method for DrawerLayout object.
Encountered the same problem and was able to fix it by specifying a width for the drawer content element inside the layout.
Without layout_width attribute the drawer did not open on the first openDrawer() call,
with the value it does.
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
<FrameLayout
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="right"
android:background="#ffffff"
android:id="#+id/drawer_content">
</FrameLayout>
Too stupid, i did set an empty layout,
problem was just as described above, when the drawer was manually dragged the first time, after that, the button worked, but without dragging it first, the navigation drawer did never open.
Don't set an empty layout.
Removing my EmptyLayout from the ListView which represented the NavigationDrawerContent made it work perfect again.
DONT
drawerContentListView.setEmptyView(getLayoutInflater().inflate(R.layout.navigation_drawer_empty_layout, null));
**** costed me more than an hour.
You can put this code inside your DrawerLayout:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
Select your NavigationView > Set visibility properties to visible
My question is simple: How to disable any event on a View in Android? (including removing its focussability, like I just want it to be there visually but be inexistant on everything else)
And does it work on a whole view tree? (like if I disable events on the root, all the events will be disabled for its children?).
Now, before you say anything I have tried all the following:
setEnabled
setFocusable
setSelected
setClickable
setActivated
And none of these methods appear to work, seriously.
I have tried them directly on a WebView, as well as on the parent layout on everything but I am still able to interact with it.
Any idea?
Thanks!
EDIT#1
The solution that consists in adding a view on top of the view that needs to be disabled doesn't work. Actually, it's still possible to click on the inner view, I have tried with a simple example:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#ff0000">
<Button
android:id="#+id/button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Click Me!"
/>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00000000"
/>
</FrameLayout>
Here it's still possible to click on the button.
EDIT#2
The reason why I want to do this is related to the following question that I asked weeks ago.
What I have is a ListViewacting as a navigation bar which is underneath a View that holds the content of my app. The problem with this implementation is that when I try to scroll through the ListView when there is a focusable view in the layer on top of it, well the ListView doesn't scroll and instead it's the top view that takes focus (That's the case when there is a
Webview or an EditText etc.).
So yes as mentioned in one of the answers, I can disable any click events on a WebView by overriding setOnTouchListener but the view remains focussed and I think this is the reason why I am still having the same issue with my navigation bar.
Simply put a view on top of your view. You can toggle it on off by setting view.visibility = gone/visible.
<FrameLayout>
<WebView/>
<FrameLayout This view will be on top/>
</FrameLayout>
Edit: Just stumpled upon this link: https://stackoverflow.com/a/3856199/969325
Basically disables all touch event for the webview. Tryed that?
Edit 2 reedit: Try to set the visibility to gone for the the top view below your listview.