Making dynamic layout for each row in ListView in android
Asked Answered
B

2

1

I have a xml file which contains basic layout for each row of ListView(which is a realtive layout and has TextView inside it).

I want to change the attributes of this layout for each row of ListView like different layout width and height of each row. I want to set the values of width and height dynamically.

Is there any way around to do this?

My xml file which I want to change, height and weight dynamically, for each view

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    android:id="@+id/item1"
    android:layout_width="wrap_content">
    <TextView
        android:id="@+id/text"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:text="text"
        android:visibility="visible"
        android:layout_width="wrap_content"
        android:textColor="#FF200010"
        android:background="#FFFCCCFF" />
</LinearLayout> 

And my full file is

package com.test.list;

import java.util.ArrayList;
import java.util.TreeSet;

import android.app.ListActivity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;


public class MultipleItemsList extends ListActivity {

    private MyCustomAdapter mAdapter;

    public Context context =getApplicationContext();


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAdapter = new MyCustomAdapter();
        for (int i = 1; i < 50; i++) {
            mAdapter.addItem("item " + i);
            if (i % 4 == 0) {
                mAdapter.addSeparatorItem("separator " + i);
            }
        }
        setListAdapter(mAdapter);
    }

    private class MyCustomAdapter extends BaseAdapter {

        private static final int TYPE_ITEM = 0;
        private static final int TYPE_SEPARATOR = 1;
        private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;

        private ArrayList<String> mData = new ArrayList<String>();
        private LayoutInflater mInflater;

        private TreeSet<Integer> mSeparatorsSet = new TreeSet<Integer>();

        public MyCustomAdapter() {
            mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }

        public void addItem(final String item) {
            mData.add(item);
            notifyDataSetChanged();
        }

        public void addSeparatorItem(final String item) {
            mData.add(item);
            // save separator position
            mSeparatorsSet.add(mData.size() - 1);
            notifyDataSetChanged();
        }

        @Override
        public int getItemViewType(int position) {
            return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
        }

        @Override
        public int getViewTypeCount() {
            return TYPE_MAX_COUNT;
        }

        @Override
        public int getCount() {
            return mData.size();
        }

        @Override
        public String getItem(int position) {
            return mData.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            int type = getItemViewType(position);
            System.out.println("getView " + position + " " + convertView + " type = " + type);
            if (convertView == null) {
                holder = new ViewHolder();
                switch (type) {
                    case TYPE_ITEM:
                            convertView = mInflater.inflate(R.layout.item1,null);
                            Log.e("Ronak", "Here1");
                            TextView t= (TextView)convertView.findViewById(R.id.text);
                            t.setWidth(100);
                            t.setHeight(600);
                            t.setText("This is first type of view");
                            holder.textView = (TextView)convertView.findViewById(R.id.text);
                            Log.e("Ronak","reached here3");
                            break;
                    case TYPE_SEPARATOR:

                          convertView = mInflater.inflate(R.layout.item1, null);
                          TextView t2= (TextView)convertView.findViewById(R.id.text);
                            t2.setWidth(200);
                            t2.setHeight(500);
                            t2.setText("This is second type of view");
                          holder.textView = (TextView)convertView.findViewById(R.id.text);
                          break;
                }
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder)convertView.getTag();
            }
            holder.textView.setText(mData.get(position));
            return convertView;
        }

    }

    public static class ViewHolder {
        public TextView textView;
    }

}
Blakeblakelee answered 6/4, 2014 at 18:13 Comment(7)
Can you share your existing code ?Biota
@FaisalAli I have added my layout fileBlakeblakelee
Note that you should change the relative params to wrap_content and change the with and height of the text view dynamicallyRoxannaroxanne
@Arkde I changed my getView() function as what you said but my program is crashing. Can you help me outBlakeblakelee
I saw the problem. The problem is the way you are using findViewById. First you have to do the convertView = mInflater.inflate(R.layout.item2,null); to inflate the xml structure into your convertView and you should use findViewById on that view TextView t2= (TextView)convertView.findViewById(R.id.text);Roxannaroxanne
@Arkde I am sorry but my app is still crashing. Can you please help me out. I have updated my file and posted the file aboveBlakeblakelee
Remove this line public Context context =getApplicationContext(); you are not using it anyway. And take care when you choose to initialize a field like that.Roxannaroxanne
R
1

You can implement a ViewHolder pattern for your adapter, and for each position, inflate your own layout.

To do that, override getView like this:

public View getView(int position, View convertView, ViewGroup parent) {            

   // A ViewHolder keeps references to children views to avoid unneccessary calls            
  // to findViewById() on each row.            
  ViewHolder holder;            
  // When convertView is not null, we can reuse it directly, there is no need            
  // to reinflate it. We only inflate a new View when the convertView supplied            
  // by ListView is null.            

  if (convertView == null) {                

    convertView = mInflater.inflate(R.layout.sample, null);   
    // Creates a ViewHolder and store references to the two children views                
    // we want to bind data to.               
   holder = new ViewHolder();                
   holder.name = (TextView) convertView.findViewById(R.id.text);               
   holder.icon = (ImageView) convertView.findViewById(R.id.icon);                
   convertView.setTag(holder);            
 } else {                
   // Get the ViewHolder back to get fast access to the TextView                
   // and the ImageView.
   holder = (ViewHolder) convertView.getTag();
 }             


  // Bind the data efficiently with the holder. 
  holder.name.setText(myElements.get(id)); 
  holder.icon.setImageBitmap( mIcon1 );

return convertView;
}  

Read more about this on the Android developer site here

EDIT:

To change the width and height of each row, you can use something like the following, for each row on bind data section :

RelativeLayout rl = (RelativeLayout) findViewById(R.id.yourId); 
rl.getLayoutParams().height = 100;
rl.getLayoutParams().width = 100;`
Roxannaroxanne answered 6/4, 2014 at 18:40 Comment(4)
I think you didn't get my question. I want the layout width and height to set dynamically and want that width and height for that row of ListView. I want to set width and height for each row. Can you further suggest how to do it?Blakeblakelee
I have edited my response to clarify how to set attributes grammaticallyRoxannaroxanne
Can you tell me, where to put the above code in getView() beacuse when I did the exact same, its giving me RunTimeExceptionBlakeblakelee
Let's say you have in your R.layout.sample, an xml that contains a RelativeLayout with id, yourId . In your ViewHolder you declare a RelativeLayout as field, and you have something like this : holder.relative = (RelativeLayout) convertView. findViewById(R.id.yourId); . After that in your bind data section you will have holder.relative.getLayoutParams().height = 100; Does this make sense?Roxannaroxanne
M
1

Making dynamic layout for each row in ListView in android

Maybe @Arkde's answer could work but i i think it's little dirty solution.

What about to create one generic layout and update / change appearance due to provided conditions? (for example due to value in current row).

different layout width and height of each row

This can by easily achieved by an usage of "margins". By "margins" i think a creation of empty Views which will work as "margins" and will determine height of row for instance. And then due to mentioned condition(s) showing or hiding them1.

What are advantages of generic layout?

  • Easier, efficient and more human-readable solution
  • You don't need to implement ViewType and ViewCount for Adapter
  • One layout for each row with changeable appearance
  • You don't need to change UI appearance from application logic in "hardcoded" way

1 When visibility of View is assigned to View.GONE it won't take place in layout whereas View.VISIBLE takes place.

Example of "margin":

<LinearLayout>
   ...
   <View 
      android:id="@+id/upperMargin"
      android:layout_width="match_parent"
      android:layout_height="40dp"
      android:background="@android:color/transparent"
   />

   ...

   <View 
      android:id="@+id/lowerMargin"
      android:layout_width="match_parent"
      android:layout_height="40dp"
      android:background="@android:color/transparent"
   />
   ...
</LinearLayout>

Note: Suggested an usage of ViewHolder is very neat and effective approach if you want to increase perfomance of ListView.

I hope that my solution will help to solve your problem you're facing now.

Monophony answered 6/4, 2014 at 19:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.