ListView scroll in marshmallow getting slow with images - android

I have a ListView with a ImageView covering the row. (I'm loading the image with Picasso. I had tried glide first but Picasso is slightly faster) It works smoothly enough on lollipop and kitkat, but on marshmallow the scroll is getting extremely slow and sometimes it is getting ANR messages.
Here's my xml for the list item:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="170dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/img_genre_bg"
android:scaleType="fitXY"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--android:background="#color/trans_black"-->
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/img_play"
android:layout_centerHorizontal="true"
android:src="#drawable/play_button"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tv_genre_name"
android:textSize="20sp"
android:textAllCaps="true"
android:layout_centerHorizontal="true"
android:textColor="#color/white"
android:layout_below="#+id/img_play"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tv_genre_id"
android:visibility="gone"/>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="#color/colorAccent"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
</RelativeLayout>
Listview:
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/lv_genre"
android:scrollbars="none"
android:divider="#null"
android:cacheColorHint="#android:color/transparent"
android:fastScrollEnabled="true"
android:persistentDrawingCache="scrolling"
android:scrollingCache="false">
</ListView>
getView():
public View getView(int position, View view, ViewGroup viewGroup) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
View rowview=view;
if(rowview==null){
rowview=inflater.inflate(R.layout.item_genre_list,viewGroup,false);
}
id = (TextView) rowview.findViewById(R.id.tv_genre_id);
TextView name = (TextView) rowview.findViewById(R.id.tv_genre_name);
ImageView img_genre_bg = (ImageView) rowview.findViewById(R.id.img_genre_bg);
ImageView img_play = (ImageView) rowview.findViewById(R.id.img_play);
opensans = Typeface.createFromAsset(context.getAssets(), "font/OpenSans-Light.ttf");
name.setTypeface(opensans);
GENRE genre=new GENRE();
genre=genres.get(position);
id.setText(genre.getGenre_id());
name.setText(genre.getGenre_name());
Picasso
.with(context)
.load(genre.getGenre_image())
.placeholder(R.drawable.loading)
.into(img_genre_bg);
return rowview;
}
I had tried loading the image on separate AsyncTask thread but still it got stuck on scrolling. Changing it to RecyclerView also didn't helped.

It's because you are creating your Typeface everytime a row is created. The overhead of creating an Asset everytime a row is created probably slows the execution.
Move this piece of code to the Constructor of your class;
opensans = Typeface.createFromAsset(context.getAssets(), "font/OpenSans-Light.ttf");
Then you can use that already created type as:
name.setTypeface(opensans);
It is better to move to RecyclerView which is more suitable since it manages memory more efficiently than ListView.

Related

Android Layout - list within a list

I am new to Android and need help in creating the following layout:
I've gone through the example of using ListFragment and ArrayAdapter but in this scenario, I want to display a list within a list. How would I create a layout like this?
You can use the ExpandableListView for this. The ExpandableListView is a List within a List:
http://developer.android.com/reference/android/widget/ExpandableListView.html
Here is a nice tutorial which explains how to use it:
http://www.androidhive.info/2013/07/android-expandable-list-view-tutorial/
You don't necessarily need a list within a list for this. You can as well use a TableLayout and modify the TableRow elements according to your requirement. Infact, I would highly recommend it. Check out this link for an example :
http://code.tutsplus.com/tutorials/android-user-interface-design-table-layouts--mobile-4788
Cheers !
For your design, you don't need to use ListView in ListView. Just use two different custom layout for ListView. One for title and other for subject data.
example:
for title layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/title1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
for data layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tv1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<TextView
android:id="#+id/tv2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView
android:id="#+id/tv3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView
android:id="#+id/tv4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView
android:id="#+id/tv5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
in your getView() from Adapter,
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Context context;
if(object.isTitle() == true){
convertView = mInflater.inflate(R.layout.title_layout, null);
//implement title layout
}
else{
convertView = mInflater.inflate(R.layout.data_layout, null);
//implement data layout
}
return convertView;
}
Hope this can help you.

Layout inflater - for only part of a layout in another layout

In my application, I have a layout list_item.xml with 3 textviews which are filled from an array and I want to inflate this 3 textviews alone in another layout. The layout in which I want to inflate is a separate xml file layout_result.xml which has a single button and android search widget.
layout file for list_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minWidth="25px"
android:minHeight="25px"
android:background="#F0F8FF"
android:orientation="horizontal"
android:layout_weight="1">
<TextView
android:id="#+id/ID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/ProductName"
android:layout_alignBottom="#+id/ProductName"
android:layout_alignParentLeft="true"
android:text="New Text" />
<TextView
android:id="#+id/Status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginRight="17dp"
android:layout_marginTop="42dp"
android:text="New Text" />
<TextView
android:id="#+id/Name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/Status"
android:layout_alignBottom="#+id/Status"
android:layout_centerHorizontal="true"
android:text="New Text" />
</RelativeLayout>
Layout for list_resut.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minWidth="25px"
android:minHeight="25px"
android:background="#F0F8FF"
android:orientation="horizontal"
android:layout_weight="1">
<SearchView
android:id="#+id/searchView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="118dp"
android:layout_marginTop="16dp" >
</SearchView>
<Button
android:id="#+id/search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignTop="#+id/searchView1"
android:layout_marginLeft="22dp"
android:text="Button" />
</RelativeLayout>
Problem I faced when I tried to include search widget and button within list_item.xml and inflating them was that the search widget and button were being inserted in each of the 3 textview element.
So I created this separate layout list_result.xml which holds only the search widget and the button.
Question, is there anyway where I can place this list_item.xml with the textview results within list_result.xml to over come this error?
My layout inflater for list_item looks like this,
public View getView(int i, View view, ViewGroup viewGroup) {
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View itemView = inflater.inflate(R.layout.list_item, viewGroup, false);
TextView txtName = (TextView) itemView.findViewById(R.id.Name);
TextView txtID= (TextView) itemView.findViewById(R.id.ID);
TextView txtStatus = (TextView) itemView.findViewById(R.id.Status);
txtName.setText(list.get(i).getProductName());
txtID.setText(list.get(i).getProductID());
txtStatus.setText(list.get(i).getStatus());
return itemView;
}
}
You should be able to just create a single layout XML. Not sure how you did it previously but you could do something like this:
<RelativeLayout>
<RelativeLayout>
<TextView/>
<TextView/>
<TextView/>
</RelativeLayout/>
<SearchView/>
<Button/>
</RelativeView>
If you want to keep the XML separate and then combine them at runtime, you still need a "container" for the inflated text views. You could use a FrameLayout as the container. Something like this for example:
results.xml:
<RelativeLayout>
<FrameLayout/>
<SearchView/>
<Button/>
</RelativeLayout>
items.xml
<RelativeLayout>
<TextView/>
<TextView/>
<TextView/>
</RelativeView/>
And then you can insert the textviews into the other layout like this:
ViewGroup container = (ViewGroup) findViewById(R.id.FrameLayoutId);
container.addChild(yourInflaterItemsView);
Hope this helps

cant get layout inflater to work

Having trouble getting layout inflater to work - wondering if someone get shed some light on why the text view is not working..
this is main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
android:background="#A9BCF5"
android:orientation="vertical"
tools:context=".MainActivity"
android:weightSum="1">
<ImageView
android:layout_width="639dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:id="#+id/imageView"
android:background="#drawable/retreatgoddess"
android:scaleType="centerCrop"
android:clickable="true"
android:cropToPadding="false"
android:longClickable="false"
android:layout_weight="0.80" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/affirmation"
android:layout_weight="0.23"
android:textAlignment="center"
android:textColor="#1728ff"
android:textStyle="normal"
android:typeface="sans"/>
</LinearLayout>
this is the method
public void fillTextView(Context context, int layoutId, int resId, String text)
{
View view= LayoutInflater.from(context).inflate(layoutId, null);
TextView textElement = (TextView) view.findViewById(resId);
textElement.setText(text);
}
i am calling it via
fillTextView(context, R.layout.main, R.id.affirmation, "i love you");
i am getting the background but no text
As you're using layout weight, you should set android:layout_height to 0px on both imageView and affirmation. Then the layout will be calculated properly.
Also, the total weight in your case is 1.03, yet you set the sum to 1.0. Change the sum or one of the layout weights.

add custom layout into DraggableGridView

I use DraggableGridView to create a ViewGroup, and I want to add my custom view into it.
Below is my custom layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/Green"
android:gravity="center_horizontal"
android:orientation="vertical" >
<ImageView
android:id="#+id/item_img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:padding="10dp"
android:scaleType="fitCenter"
android:src="#drawable/default_avatar" />
<TextView
android:id="#+id/item_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/Gray"
android:gravity="center"
android:text="Name" />
</LinearLayout>
As you can see, the background color of LinearLayout should be green, which is displayed correctly in the ViewGroup.
However, the ImageView and the TextView aren't displayed on the screen, neither even the background color of the TextView.
And this is my code to add the custom view:
LayoutInflater vi = (LayoutInflater) this
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = vi.inflate(R.layout.item, null);
TextView textView = (TextView) v
.findViewById(R.id.item_name);
textView.setText(contact.getName());
textView.setTextColor(Color.WHITE);
gridView.addView(v);
What's the problem? Thank you.

Why are ViewPager and GridLayout with 30 Elements very slow?

I have a ViewPager and a GridView to show a monthly calendar. The GridView has about 30 elements and swipe to left and right is very slow. I made a sample project to test without any database access and it is slow, too. Maybe someone sees the problem or do I have to make an asynchronous load of the pages?
That is the layout for one element in the GridView. I want to show 9 little 5x5 pixel icons in any element, but without them, it is slow, too.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="#+id/Layout1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:padding="1dp">
<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content">
<TextView android:id="#+id/date" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginLeft="5dp" android:gravity="center_vertical" android:textSize="14sp" android:textStyle="bold"/>
</LinearLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
<ImageView android:id="#+id/dot1" android:layout_width="wrap_content" android:drawingCacheQuality="auto" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:background="#ffffff" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:paddingBottom="1dp" android:paddingLeft="1dp" android:paddingTop="5dp"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
<ImageView android:id="#+id/dot2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
<ImageView android:id="#+id/dot3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_gravity="right" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
<ImageView android:id="#+id/dot4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_gravity="center_horizontal" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
<ImageView android:id="#+id/dot5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_gravity="left" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
<ImageView android:id="#+id/dot6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_gravity="left" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
<ImageView android:id="#+id/dot7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_gravity="left" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
<ImageView android:id="#+id/dot8" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_gravity="left" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:background="#ffffff" android:paddingBottom="1dp" android:paddingLeft="1dp"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff">
<ImageView android:id="#+id/dot9" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_gravity="left" android:background="#ffffff" android:paddingBottom="5dp" android:paddingLeft="1dp"/>
</RelativeLayout>
</LinearLayout>
That's the instantiateItem of GridView adapter:
public Object instantiateItem(ViewGroup container, int position) {
GridView gv = new GridView(m_context);
gv.setAdapter(new BaseAdapter() {
#Override
public View getView(int pos, View convertView, ViewGroup parent) {
View v;
if (convertView == null) {
// if it's not recycled, initialize some attributes
v = mInflater.inflate(R.layout.calendar_month_item, null);
} else {
v = convertView;
}
TextView dayView = (TextView) v.findViewById(R.id.date);
dayView.setText(pos + "");
return v;
}
#Override
public long getItemId(int p) {
return 0;
}
#Override
public Object getItem(int p) {
return null;
}
#Override
public int getCount() {
return 30;
}
});
gv.setNumColumns(5);
((ViewPager) container).addView(gv);
return gv;
}
I looked around for a bit and I think I found exactly what you are looking for:
content XML, you will probably have to change or redo your layout as it is turns out quite large, as you said.
CalendarAdapter.java, in line 112 you can see that they added an icon there. Just change the code around so you can add your nine icons programmatically instead of having all of these layouts.
How to implement CalendarView in Android
Which version of Android do you use? This is important, because there is a Calendar in Android 3.0, is has bugs, but not so critical.
About your question, if I well understood it. Look at your XML, it contains 11 layouts, and this is just for only one element. If you have them many, imagine the amount of work the Android system must do, to inflate all your elements. And after that, when you swipe, you reuse the elements, and Android must refresh the ones that are not valid. This is a lot of work. (11 layouts * 30 = 330 layouts to be refreshed or inflated). As Android developer documentation says, you must always use as less layouts to wrap your elements, as possible!
Anyway, your approach is not correct. I can suggest you to look at the source code of the CalendarView in Android 3+, but to give you a hint:
Create a layout for a week, not for every day(like in CalendarView). By doing this, Android will have to refresh only n elements(You'll choose how many weeks you want to display at a moment of time), not 30. This layout must contain 7 views for every day.
Hope you get some idea from this.
First of all, your layout is a bit strange, as others have stated.
You really don't need the schema for any layout sub elements.
Having multiple nested relative or linear layouts when not needed will be quite expensive, especially when wrapped in a GridView wrapped in a ViewPager.
Second of all, the ViewPager and PagerAdapter are a little bit tricky.
When you have the default number of offscreen pages, you will layout all three of them at the same time. If you set it higher, you will be layouting even more views.
Make sure that you correctly remove views in destroyItem.
Implement the isViewFromObject method correctly, since otherwise your views will be discarded when you page, causing more cost of recreating them.
Going with #CommonsWare's advice is probably a good idea. TraceView is a bit difficult to use, but even so it will probably yield some clues.

Categories

Resources