I have an android activity with a gridview, each cell contains a textview with a single character (so there are around 60-70 characters/cells on the screen at a time). The scrolling of the gridview is unacceptably slow and unsmooth. I tried replacing the gridview with a listview, and the scrolling of the listview is much faster. How can i speed this up?
The activity layout is:
<?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="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<GridView
android:id="#+id/gridView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="56dp"
android:numColumns="auto_fit" >
</GridView>
</LinearLayout>
And inside each cell is this layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="40sp" />
</LinearLayout>
And the code for the activity is:
public class TestGridActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.grid_activity);
GridView gridView = (GridView) findViewById(R.id.gridView1);
ArrayList<Map<String, String>> list = getData();
SimpleAdapter arrayAdapter = new SimpleAdapter(this, list,
R.layout.grid_layout, new String[] { "literal"},
new int[] { R.id.textView1});
gridView.setAdapter(arrayAdapter);
}
}
edit: pks asking to post adapter code, the above code I have used a generic simpleAdapter, but i have tried a custom view, which didn't help.
public class GridAdapter extends BaseAdapter {
private Context context;
private ArrayList<Map<String, String>> list;
private LayoutInflater inflater;
public static class ViewHolder {
TextView textView1;
int position;
}
public GridAdapter(Context c, ArrayList<Map<String, String>> l) {
context = c;
list = l;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return list.size();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null){
convertView = inflater.inflate(R.layout.grid_layout, null);
holder = new ViewHolder();
holder.textView1 = (TextView) convertView.findViewById(R.id.textView1);
holder.position = position;
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView1.setText(list.get(position).get("character"));
return convertView;
}
}
I've also tried creating all the views in advance, and that didn't help scrolling speed.
Related
I really need help, Listview which I used in my app is very slow while scrolling how can I fix it ? I reduce the images, use viewholder but nothing change..
I am beginner in android : )
also get this message : I/Choreographer: Skipped 32 frames! The application may be doing too much work on its main thread.
Here is my code
public class asker extends Activity {
ListView list;
String[] askerbaslik;
int[] images =
{R.drawable.barbar_icon, R.drawable.okcu_icon,
R.drawable.dev_icon,
R.drawable.goblin_icon,
R.drawable.duvar_kirici_icon, R.drawable.balon_icon,
R.drawable.buyucu_icon, R.drawable.sifaci_icon,
R.drawable.ejderha_icon,
R.drawable.pekka_icon, R.drawable.yavruejder_icon, R.drawable.madenci_icon};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listview);
Resources res = getResources();
askerbaslik = res.getStringArray(R.array.asker_adlari);
list = (ListView) findViewById(R.id.listview);
ilyasadapter adapter = new ilyasadapter(this, askerbaslik, images);
list.setAdapter(adapter);
}
class ilyasadapter extends ArrayAdapter<String> {
Context context;
int[] images;
String[] titlearray;
ilyasadapter(Context c, String[] titles, int imgs[]) {
super(c, R.layout.liste_tasarim, R.id.asker_baslik, titles);
this.context = c;
this.images = imgs;
this.titlearray = titles;
}
class MyViewHolder {
TextView mytitle;
ImageView myimage;
MyViewHolder(View v) {
mytitle = (TextView) v.findViewById(R.id.asker_baslik);
myimage = (ImageView) v.findViewById(R.id.asker_icon);
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
MyViewHolder holder = null;
if (row == null) {
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.liste_tasarim, parent, false);
holder = new MyViewHolder(row);
row.setTag(holder);
Log.d("test", "creating a new row");
} else {
holder = (MyViewHolder) row.getTag();
Log.d("test", "Recycling stuff");
}
holder.myimage.setImageResource(images[position]);
holder.mytitle.setText(titlearray[position]);
return row;
}
}
}
Single row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_marginTop="5dp"
android:id="#+id/asker_icon"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="#drawable/barbar_icon"
android:background="#drawable/rounded_corner"
></ImageView>
<TextView
android:id="#+id/asker_baslik"
android:layout_marginTop="40dp"
android:layout_marginLeft="30dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Asker ismi"
android:textColor="#ffffff"
android:textSize="15dp"
android:fontFamily="sans-serif"
/>
</LinearLayout>
my listview
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#455A64" >
<ListView
android:id="#+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#263238"
android:dividerHeight="2px"
/>
</RelativeLayout>
Please try to remove the icons from the listview and see if that improves performance. You may be using images with too high of a resolution. I too have noticed performance anomalies when using high-resolution images.
Let me know if that helps. If not, there are a few other things that we can try.
I am using Custom Adapter for ListView. Each ListView item contains ImageButton which will delete that Item. Now, I want to refresh full ListView when anyone click on ImageButton. How to do this ?
Or is there anyway to check ImageButton click on ListView onItemClickListener ? I have already tried notifyDataSetChanged in Custom Adapter but I can't find any changes.
Custom Item xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/lightish"
android:orientation="vertical"
android:padding="10dp" >
<ImageButton
android:id="#+id/imgDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginBottom="-20dp"
android:background="#android:color/transparent"
android:contentDescription="#string/app_name"
android:src="#drawable/ic_delete" />
<TextView
android:id="#+id/tvName"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#color/black"
android:textSize="20sp" />
</LinearLayout>
Custom Adapter java :
public class MyAddressesAdapter extends BaseAdapter {
private Activity activity;
private ArrayList<HashMap<String, String>> data;
private static LayoutInflater inflater = null;
View vi;
HashMap<String, String> address;
public MyAddressesAdapter(Activity a,
ArrayList<HashMap<String, String>> arlData) {
activity = a;
data = arlData;
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public int getCount() {
return data.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, final View convertView, ViewGroup parent) {
vi = convertView;
if (convertView == null)
vi = inflater.inflate(R.layout.my_address_view, parent, false);
final TextView tvName = (TextView) vi.findViewById(R.id.tvName);
address = new HashMap<String, String>();
address = data.get(position);
tvName.setText(address.get("PersonName"));
return vi;
}
In main activity java :
myAddressesAdapter = new MyAddressesAdapter(
getActivity(), addressList);
lvMyAddresses.setAdapter(myAddressesAdapter);
put code in getview method in adapter
imageButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//your code here
data.remove(position);
notifyDataSetChanged();
}
});
I created this class called GeoArea, which is suppose to store "Geographical Area" that have children Geographical Areas, this is fairly strait foward:
public class GeoArea {
public String id;
public String name;
public List<GeoArea> subGeoAreas;
public GeoArea parentGeoArea;
public GeoArea(String id) {
this.id = id;
name = id;
subGeoAreas = new LinkedList<GeoArea>();
}
#Override
public String toString() {
return name;
}
}
I have created the following Layout to render it on Android, the idea here is to for each GeoArea to recursively render it self and then it's children GeoArea in a listView:
//layout_geo_area.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/txtGeoAreaName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:gravity="left"
android:text="Geo Area Name"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ListView
android:id="#+id/listViewChildGeoAreas"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="#+id/txtGeoAreaName"
android:gravity="left" >
</ListView>
</RelativeLayout>
This is my adapter I created for GeoArea to be displayed in a listView:
public class AdapterGeoArea extends ArrayAdapter<GeoArea>{
private ArrayList<GeoArea> _myGeoArea;
private Context _myContext;
LayoutInflater _inflater;
public AdapterGeoArea(Context context, ArrayList<GeoArea> myGeoArea) {
super(context, 0, myGeoArea);
_myGeoArea = myGeoArea;
_inflater = LayoutInflater.from(context);
_myContext = context;
}
public int getCount() {
return _myGeoArea.size();
}
public View getView(int position, View convertView, ViewGroup parent) {
GeoAreaLayoutHolder holder;
if (convertView == null) {
convertView = _inflater.inflate(R.layout.layout_geo_area,parent,false);
holder = new GeoAreaLayoutHolder();
holder.txtGeoAreaName = (TextView)convertView.findViewById(R.id.txtGeoAreaName);
holder.txtGeoAreaName.setTag(convertView);
holder.listViewChildGeoAreas = (ListView)convertView.findViewById(R.id.listViewChildGeoAreas);
holder.listViewChildGeoAreas.setTag(convertView);
} else {
holder = (GeoAreaLayoutHolder) convertView.getTag();
}
GeoArea curGeoArea = _myGeoArea.get(position);
holder.txtGeoAreaName.setText(curGeoArea.name);
if(curGeoArea.subGeoAreas.size()>0){
ArrayList<GeoArea> testList = new ArrayList<GeoArea>();
AdapterGeoArea adapter = new AdapterGeoArea(_myContext, testList);
for(GeoArea childGeoArea:curGeoArea.subGeoAreas){
testList.add(childGeoArea);
}
holder.listViewChildGeoAreas.setAdapter(adapter);
}
return convertView;
}
static class GeoAreaLayoutHolder {
public TextView txtGeoAreaName;
public ListView listViewChildGeoAreas;
}
}
And here is my Activity that I am using to set it all up:
public class ActivityGeoAreas extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_geo_area);
GeoArea.searchTerm = "Bar & Grill";
GeoArea torontoArea = new GeoArea("cityOfToronto");
ArrayList<GeoArea> testList = new ArrayList<GeoArea>();
testList.add(torontoArea);
AdapterGeoArea adapter = new AdapterGeoArea(this, testList);
ListView lv = (ListView) findViewById(R.id.listViewChildGeoAreas);
lv.setAdapter(adapter);
}
}
When I try to run it, I get the error nullPointerException on the line:
holder.txtGeoAreaName.setText(curGeoArea.name);
What am I doing wrong?
You may want to check ExpandableListView may suit your needs better
http://developer.android.com/reference/android/widget/ExpandableListView.html
An example # http://www.androidhive.info/2013/07/android-expandable-list-view-tutorial/
Continuing from my previous answer to your question ( i though that solved your problem)
To display just name in your listview
list_row.xml // this is the layout with textview to be inflated in getView.
Each row will have textview
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/textGeoArea"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="14dp"
android:text="Choose Area"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
layout_geo_area.xml // with only listview no textview
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="#+id/listViewChildGeoAreas"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:gravity="left" >
</ListView>
</RelativeLayout>
Now your adapter class
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.list_row,parent,false);
// inflate list_row.xml with textview
holder = new ViewHolder();
holder.tv = (TextView)convertView.findViewById(R.id.textGeoArea);
holder.setTag(convertView);
}
else {
holder = (ViewHolder) convertView.getTag();
}
GeoArea curGeoArea = _myGeoArea.get(position);
holder.tv.setText(curGeoArea.name);
return convertView;
}
static class ViewHolder // use a view holder for smooth scrolling an performance
{
TextView tv;
}
You have a custom adapter.
public class AdapterGeoArea extends ArrayAdapter<GeoArea>{
Now you set the adapter to listview like below
AdapterGeoArea adapter = new AdapterGeoArea(this, testList);
ListView lv = (ListView) findViewById(R.id.listViewChildGeoAreas);
lv.setAdapter(adapter);
So why do you need the below. remove these
if(curGeoArea.subGeoAreas.size()>0){
ArrayList<GeoArea> testList = new ArrayList<GeoArea>();
AdapterGeoArea adapter = new AdapterGeoArea(_myContext, testList);
for(GeoArea childGeoArea:curGeoArea.subGeoAreas){
testList.add(childGeoArea);
}
holder.listViewChildGeoAreas.setAdapter(adapter);
I am using the following code.
Adapter.java class
public class Adapter extends ArrayAdapter<String> {
private java.util.List<String> List;
private Context context;
public Adapter(List<String> list, Context ctx) {
super(ctx, R.layout.row_item, list);
this.List = list;
this.context = ctx;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
NameHolder holder = new NameHolder();
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.row_item, null);
TextView tv = (TextView)v.findViewById(R.id.textView1);
LinearLayout ll = (LinearLayout) v.findViewById(R.id.myLayout);
holder.topic_name = tv;
holder.myLayout = ll;
v.setTag(holder);
}
else
holder = (NameHolder) v.getTag();
String topic = List.get(position);
holder.topic_name.setText(topic);
ImageView imgUsers = new ImageView(context);
imgUsers.setImageResource(R.drawable.ic_launcher);
holder.myLayout.addView(imgUsers);
return v;
}
private static class NameHolder {
public TextView topic_name;
public LinearLayout myLayout;
}
}
MainActivity.java class :
public class MainActivity extends Activity {
ListView lv;
ArrayList<String> list;
Adapter myAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView)findViewById(R.id.listView1);
list = new ArrayList<String>();
for(int i=0; i<10;i++){
list.add("Name"+i);
}
myAdapter = new Adapter(list, MainActivity.this);
lv.setAdapter(myAdapter);
lv.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this,"lets see"+list.get(arg2), Toast.LENGTH_SHORT).show();
}
});
}
}
activity_main.xml :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
row_item.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" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
<HorizontalScrollView
android:id="#+id/horizontalScrollView1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:orientation="horizontal"
android:id="#+id/myLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
></LinearLayout>
</HorizontalScrollView>
</LinearLayout>
Now, the expected output is a single image in every listItem, which tend to go to infinite.
And the onItemClickListener is not working as well.
Also, onItemClickListener works if we don't add HorizontalScrollView.
Writing onClickListener() in Adapter itself helps little as it works unpredictably.
Also, on adding a print statement in getView() and observing in Logcat, I find it difficult to understand the pattern in which getView() is called.
These are the problems I face. I require a working code of the things I wish to apply. Thanks in advance.
// Replace this code
public View getView(int position, View convertView, ViewGroup parent) {
NameHolder holder;
if (convertView == null) {
holder = new NameHolder();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.row_item, null);
holder.topic_name = (TextView)convertView.findViewById(R.id.textView1);
holder.myLayout = (LinearLayout) convertView.findViewById(R.id.myLayout);
convertView.setTag(holder);
}
else
holder = (NameHolder) convertView.getTag();
String topic = List.get(position);
holder.topic_name.setText(topic);
ImageView imgUsers = new ImageView(context);
imgUsers.setImageResource(R.drawable.ic_launcher);
holder.myLayout.removeAllViews();
holder.myLayout.addView(imgUsers);
convertView.setTag(holder);
return convertView;
}
Looks like you are adding an ImageView every single time here
ImageView imgUsers = new ImageView(context);
imgUsers.setImageResource(R.drawable.ic_launcher);
holder.myLayout.addView(imgUsers);
Why don't you include the ImageView as part of your list item layout?
I'd like to customize some settings (color, margin) for some items on a listview in a listactivity after or before setting the adapter. How can I do that? Is there any function that can I override?
Thank you.
you can use an own listadapter.. http://www.vogella.com/articles/AndroidListView/article.html
This is how you can do to customize item of your list:
Layout of your list:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
Layout of each item:
<?xml version="1.0" encoding="utf-8"?>
<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/text"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
/>
</LinearLayout>
And finaly your activity:
public class MyActivity extends ListActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_activitty);
final List<String> list = new ArrayList<String>();
list.add("test");
list.add("test");
list.add("test");
final CustomAdapter adapter = new CustomAdapter(this, list);
final ListView listView = getListView();
listView.setAdapter(adapter);
}
public class CustomAdapter extends BaseAdapter {
private Context mContext;
private List<String> mList;
public CustomAdapter(Context context, List<String> list) {
mContext = context;
mList = list;
}
#Override
public int getCount() {
return mList.size();
}
#Override
public String getItem(int position) {
return mList.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final Holder holder;
if (convertView == null) {
// if it is the first time you create the row
// you get the layout of each row here
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list, null);
// you keep your layout in a holder
holder = new Holder();
holder.mText = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {
// if the row has already been created, you get it from the holder
holder = (Holder) convertView.getTag();
}
// you do what you want with the content
holder.mText.setText(getItem(position));
holder.mText.setTextColor(Color.BLUE);
return convertView;
}
private class Holder {
public TextView mText;
}
}
}