Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enlarging the middle element in a listview - android?

Im trying to Enlarge the Centre most listview item. (Eventually, I hope to expand this into a sort of simple and light 3D effect that elements will appear to zoom in when in the middle of the screen and zoom out when they are down below, Total black background and text items zooming in and out... )

So, I've made a dummy listview, Each row is a textview element. Below, I have a custom Adapter.

The only relevant modification I've done is:

In the OnCreate(), I pass the following variable to the adapter:

int v = (listview.getLastVisiblePosition() - listview.getFirstVisiblePosition()) + 1;

And in the Adapter's getView(), I've got this :

if (position == x/2) {
        textView.setHeight(100);
    }

Anyways here's all of the code, I've skipped the imports: (It's not that big !... most of it is quite general)

MainActivity.java :

    public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final ListView listview = (ListView) findViewById(R.id.listview);
        String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
                "Blackberry", "WebOS", "Ubuntu", "Windows7","iPhone", "WindowsMobile",
                "Blackberry", "WebOS", "Ubuntu", "Windows7","iPhone", "WindowsMobile",
                "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X" };

        final ArrayList<String> list = new ArrayList<String>();
        for (int i = 0; i < values.length; ++i) {
            list.add(values[i]);
        }
        int v = (listview.getLastVisiblePosition() - listview.getFirstVisiblePosition()) + 1;


        final MySimpleArrayAdapter adapter = new MySimpleArrayAdapter(this, v, R.layout.row, list);
        listview.setAdapter(adapter);

        listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, final View view,
                    int position, long id) {

            }

        });
    }
}

MySimpleArrayAdapter.java

 public class MySimpleArrayAdapter extends ArrayAdapter<String> {
    private final Context context;
    private final int x;

    HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();

    public MySimpleArrayAdapter(Context context, int a, int textViewResourceId,
            List<String> objects) {
        super(context, textViewResourceId, objects);
        this.context = context;
        this.x = a;
        for (int i = 0; i < objects.size(); ++i) {
            mIdMap.put(objects.get(i), i);
        }

    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View rowView = inflater.inflate(R.layout.row, parent, false);
        TextView textView = (TextView) rowView.findViewById(R.id.textView1);

        if (position == x/2) {
            textView.setHeight(100);
        }

        return rowView;
    }

    @Override
    public long getItemId(int position) {
        String item = getItem(position);
        return mIdMap.get(item);
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }
}

Further, in my activity_main.xml I just have a single listview, and in "row.xml" I have a single textview..

Problems: Why is no text appearing? I clearly added the list values to the textview .

Why is the top most element big and not the middle one, I specifically set it to the middle one ?

Here's the screenie that my current code gives:

LOGCAT Error - KMDev's solution

10-06 17:54:02.529: E/AndroidRuntime(19596): FATAL EXCEPTION: main
10-06 17:54:02.529: E/AndroidRuntime(19596): java.lang.RuntimeException: Unable to start activity ComponentInfo{mple.finala/mple.finala.MainActivity}: java.lang.NullPointerException
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2077)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2104)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.app.ActivityThread.access$600(ActivityThread.java:134)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1247)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.os.Handler.dispatchMessage(Handler.java:99)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.os.Looper.loop(Looper.java:154)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.app.ActivityThread.main(ActivityThread.java:4624)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at java.lang.reflect.Method.invokeNative(Native Method)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at java.lang.reflect.Method.invoke(Method.java:511)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:809)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:576)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at dalvik.system.NativeStart.main(Native Method)
10-06 17:54:02.529: E/AndroidRuntime(19596): Caused by: java.lang.NullPointerException
10-06 17:54:02.529: E/AndroidRuntime(19596):    at mple.finala.MainActivity$2.onScroll(MainActivity.java:79)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.widget.AbsListView.invokeOnItemScrollListener(AbsListView.java:1297)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.widget.AbsListView.setOnScrollListener(AbsListView.java:1286)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at mple.finala.MainActivity.onCreate(MainActivity.java:58)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.app.Activity.performCreate(Activity.java:4479)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1050)
10-06 17:54:02.529: E/AndroidRuntime(19596):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2041)
10-06 17:54:02.529: E/AndroidRuntime(19596):    ... 11 more

EDIT 2:

Complete Code, still giving null exception.

http://codeviewer.org/view/code:373d

like image 911
Zenis Avatar asked Oct 06 '13 04:10

Zenis


1 Answers

A few things need to be addressed.

  1. You need to use the ViewHolder pattern. That and the things you really need to know about ListView are in the GoogleIO 2010 - The World of ListView video.

  2. It's not really a good idea to adjust the layout of an item in getView as that will negatively impact the view recycler that ListView uses. That is also in the video. I would really encourage you to try and find a different way to display what you want to display.

Setting your text

  1. You're passing the x-dimension you want for an item in to your Adapter's constructor instead of supplying it an actual item resource id. ArrayAdapter will set your text for you if you pass in the right arguments. For a simple example using a string resource and the default provided list item layouts:

    String[] mData = getResources().getStringArray(R.array.list_examples);
    ArrayAdapter<String> mBaseAdapter = 
                new ArrayAdapter<String>(context, 
                                         android.R.layout.simple_list_item_1,
                                         android.R.id.text1,
                                         mListData);
    
  2. If I were you, I'd just let the adapter set the text and grab that item layout by calling super like so:

    @Override
    public View getView(int position, View convertView, ViewGroup viewGroup){
        convertView = super.getView(position,convertView, viewGroup);
        //other code here
    }
    

If you really want to do it yourself, here it is with the ViewHolder pattern

    //This is the ViewHolder
    static class Holder{
        TextView mText;
    }


    @Override
    public View getView(int position, View convertView, ViewGroup viewGroup){

         convertView = super.getView(position, convertView, viewGroup);

         if (convertView == null){
              // If you reach this, it most likely means you're not supplying the
              // adapter the layout and textview resource it needs. You can
              // just comment out your getView override and see if your text gets
              // displayed.
         }

         Holder mHolder;
         if(convertView.getTag() == null){
            mHolder = new Holder();
            mHolder.mText = (TextView)convertView.findViewById(android.R.id.text1);
            convertView.setTag(mHolder);
         } else {
            mHolder = (Holder) convertView.getTag();
         }

         mHolder.mText.setText(getItem(position).toString());
    }

About getXVisibleItem methods

// These are both implemented in AdapterView which means
// they're flat list positions
int firstVis = mList.getFirstVisiblePosition(); 
int lastVis = mList.getLastVisiblePosition();

/* This is the "conversion" from flat list positions to ViewGroup child positions */
int count = lastVis - firstVis; 

 /*  getChildAt(pos) is implemented in ViewGroup and has a different meaning for 
 *  its position values. ViewGroup tracks visible items as children and is 0 indexed. 
 *  This means you'll have 0 - X positions where X is however many items it takes 
 *  to fill the visible area of your screen; usually less than 10. */
 View listItem = mList.getChildAt(count - ((int)count/2)); // This will get the middle 
                                                        // item for you to adjust as 
                                                        // you want.

To find the item in the middle of the screen, you just need to take your visible item count and subtract half of that count as commented above.

Set Height Implementation

You will want to set an AbsListView.OnScrollListener on your ListView and when the scroll stops, apply your adjustments to the middle item. Ex:

mList.setOnScrollListener(new OnScrollListener(){
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState){

    }       

    @Override
    public void onScroll(AbsListView view, 
                         int firstVisibleItem, 
                         int visibleItemCount,
                         int totalItemCount){

        //Note that this is called when the scroll completes. 
        //It gives us the positions we want so just calculate
        //the middle item, grab it and adjust the height directly
        //or by increasing margins if the item layout_height is 
        //wrap_content. I'm not positive the margins do what you
        // need, but setting the height directly should.
        LinearLayout listItem = mList // <-- Replace LinearLayout with your layout
                      .getChildAt(visibleItemCount - ((int)visibleItemCount/2));

        //Make sure you use the right LayoutParams for the listItem Layout
        //If your listItem is a RelativeLayout for instance, you would use:
        // listItem.setLayoutParams(
        //      new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT,
        //                                      theHeightYouWantHere));
        listItem.setLayoutParams(
                new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
                                              theHeightYouWantHere)); 

        }
});

I've made a few tangentially related blog posts about ListView and ExpandableListView which have full code examples on how to refresh visible items (the example code just changes the text/background color of the item) which uses the childAt strategy. Pardon the personal blog link and the styling of it (it's new and needs more work :), but the code works well.

Edit 2: For android.R.simple_list_item_1

This does exactly what you want as long as you're using a TextView for the item layout (android.R.simple_list_item_1 is a TextView). If you decide to use a ViewGroup layout, you'll need to replace item.setHeight with the LayoutParams method.

mList.setOnScrollListener(new OnScrollListener(){
    @Override
    public void onScrollStateChanged(final AbsListView view, final int scrollState){

    }

    @Override
    public void onScroll(final AbsListView view, 
                         final int firstVisibleItem,
                         final int visibleItemCount,
                         final int totalItemCount){
        if (visibleItemCount != 0){

             Log.i( TAG, "firstVisibleItem: "
                         + firstVisibleItem
                         + "\nvisibleItemCount: "
                         + visibleItemCount);

             final int midPosition = visibleItemCount - (visibleItemCount / 2); 
             final TextView listItem = (TextView) mList.getChildAt(midPosition);
             listItem.setHeight(500); //Only works if you're using
                                      //android.R.simple_list_item_1 since it's
                                      //not a ViewGroup, but rather a TextView

             int count = visibleItemCount;
             while (count >= 0){ // Here we need to loop through and make sure
                                 // all recycled items are returned to their 
                                 // original height. 
                 final TextView item = (TextView) mList.getChildAt(count);
                 if (item != null && count != midPosition){
                     item.setHeight(100);
                 }
                 count--;
              }
        }
    }
});
like image 85
KMDev Avatar answered Nov 24 '22 17:11

KMDev