I implemented a GridView
inside PageViewer
, but scrolling is not as smooth as it should be. Views inside GridView
are intercepting touches.
This is driving me crazy I have tried everything and while one thing worked it is not good solution in the end. First here is my code.
apps_grid.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<GridView
android:id="@+id/appGrid"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/ic_launcher_background"
android:horizontalSpacing="4dp"
android:numColumns="4"
android:paddingTop="10dp"
android:stretchMode="columnWidth"
android:verticalSpacing="15dp"/>
</LinearLayout>
app_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/icon"
android:layout_width="54dp"
android:layout_height="54dp"
android:layout_gravity="center_horizontal"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="5dp"
android:ellipsize="end"
android:gravity="center"
android:paddingEnd="15dp"
android:paddingStart="15dp"
android:singleLine="true"
android:text="title"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
activity_main.xml
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="45dp"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
AppModel.java
public class AppModel {
private String label;
private String pkg;
private Drawable mIcon;
public AppModel(String name, String pkg, Drawable icon) {
this.label = name;
this.pkg = pkg;
this.mIcon = icon;
}
public AppModel(String name) {
this.label = name;
}
public String getLabel() {
return this.label;
}
public String getPkg() {
return this.pkg;
}
public Drawable getIcon() {
return mIcon;
}
}
AppListAdapter.java
public class AppListAdapter extends BaseAdapter {
private ArrayList<AppModel> apps;
private LayoutInflater layoutInflater;
public AppListAdapter(Context context, ArrayList<AppModel> apps) {
this.apps = apps;
this.layoutInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return apps.size();
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public Object getItem(int position) {
AppModel app = apps.get(position);
if(app == null) {
return null;
}
return app;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
AppModel app = apps.get(position);
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.app_item, null);
}
if(app == null) {
return convertView;
}
((TextView)convertView.findViewById(R.id.text)).setText(app.getLabel());
return convertView;
}
}
TestPagerAdapter.java
public class TestPagerAdapter extends PagerAdapter {
private Context mContext;
private LayoutInflater inflater;
private ArrayList<AppModel> apps;
private int perPage = 12;
public TestPagerAdapter(Context context) {
this.mContext = context;
this.inflater = LayoutInflater.from(context);
}
@Override
public Object instantiateItem(ViewGroup parent, int position) {
ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.apps_grid, parent, false);
ArrayList<AppModel> pageApps = getAppsForPage(position);
if(pageApps == null) {
return layout;
}
final GridView appGrid = (GridView) layout.findViewById(R.id.appGrid);
final AppListAdapter gridAdapter = new AppListAdapter(mContext, pageApps);
appGrid.setVerticalScrollBarEnabled(false);
appGrid.setAdapter(gridAdapter);
parent.addView(layout);
return layout;
}
@Override
public void destroyItem(ViewGroup parent, int position, Object view) {
parent.removeView((View) view);
}
@Override
public int getCount() {
if(apps != null) {
return (int) Math.ceil((double) apps.size() / perPage);
} else {
return 0;
}
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
public void setData(ArrayList<AppModel> data) {
if(data != null) {
this.apps = data;
}
}
public ArrayList<AppModel> getAppsForPage(int page) {
if(apps == null) {
return null;
}
int size = apps.size();
int offset = getOffset(page);
ArrayList<AppModel> pageApps = new ArrayList<AppModel>(size);
for(int i = 0; i < perPage; i++) {
if(offset+i < size) {
pageApps.add(apps.get(offset+i));
}
}
return pageApps;
}
public int getOffset(int page) {
return perPage*page;
}
}
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
TestPagerAdapter mPAdapter = new TestPagerAdapter(this);
viewPager.setAdapter(mPAdapter);
ArrayList<AppModel> items = new ArrayList<AppModel>(20);
for(int i=0;i<20;i++) {
AppModel app = new AppModel(""+i);
items.add(app);
}
mPAdapter.setData(items);
mPAdapter.notifyDataSetChanged();
}
So, here is what I tried:
I replaced GridView
with RecyclerView
.
Added all this attributes on EVERY View
android:clickable="false"
android:contextClickable="false"
android:defaultFocusHighlightEnabled="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:focusedByDefault="false"
android:isScrollContainer="false"
android:longClickable="false"
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
android:touchscreenBlocksFocus="false"
Overriding OnTouchListener (and many other) on GridView
and returning true
.
probably tried some other things that I cannot remember right now.
The only thing that worked was settings android:filterTouchesWhenObscured="true"
attribute on GridView
while some other applications view was on top (so condition "WhenObscured" was satisfied)
When I tried this my ViewPager
started scrolling very smoothly, in other cases it sometimes does not scroll (probably when GridView
intercepts it).
Note that items inside GridView
in the end must be clickable.
UPDATE
https://github.com/mpa4hu/GridViewPager this is a github link to the project, to simulate the problem scroll forward and backward with gesture similar to this picture
Recycler view recycles the view and doesn't recreate the view until required. It just re-binds the view.
RecyclerView was created as a ListView improvement, so yes, you can create an attached list with ListView control, but using RecyclerView is easier as it:
Reuses cells while scrolling up/down - this is possible with implementing View Holder in the listView adapter, but it was an optional thing, while in the RecycleView it's the default way of writing adapter.
Decouples list from its container - so you can put list items easily at run time in the different containers (linearLayout, gridLayout) with setting LayoutManager.
Example:
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//or
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 2));//number
of columns in a grid layout
There is more about RecyclerView, but I think these points are the main ones.
So, to conclude, RecyclerView is a more flexible control for handling "list data" that follows patterns of delegation of concerns and leaves for itself only one task - recycling items.
You can refer about its advantages here : RecyclerView over ListView
I have this silver bullet class to solve my own nestedScrolling issues:
class NonScrollableRecyclerView : RecyclerView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun onInterceptTouchEvent(event: MotionEvent): Boolean = false
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean = false
}
So you can create a custom-non-scrollable GridView and use it instead a regular GridView:
class NonScrollableGridView : GridView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun onInterceptTouchEvent(event: MotionEvent): Boolean = false
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean = false
}
Due to appabarScrollingBehaviour the scroll of gridView lags ... So you have disable the nested scrolling behaviour of your gridView , to do so...just add..
ViewCompat.setNestedScrollingEnabled(listView/gridview,false);
in your case use your gridView ...
(add Android Support v4 Library 23.1 or +) here
These will hopefully allow your GridView you to scroll smoothly...
Happy Coding :)
Edit
You can also try
android:fastScrollEnabled="true"
to your gridView layout.. in xml.. this might help..
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With