简体   繁体   中英

How to change the background color of a non-visible item in ListView in Android

I'm trying to make a navigation drawer that changes the background color of items in a ListView based on integers received from another part of the application – ie not based on what item is clicked. I have managed to color two separate items in the navigation drawer, but apparently my way only works for items that are visible. I tried doing a work-around that involved scrolling to the desired item before changing the background color, but I couldn't figure out a way to calculate what number the item had after the scroll-down, since getChildAt(position) seems to start at the first visible item.

Is there any way I can solve this without having to re-do the entire navigation drawer? I searched for a few guides on how to do a sidebar menu like this, but most meant I had to set up static items in an XML – and since my list needs to change regularly I figured this would be the easiest way, but for all I know it might as well be a bad way to go about it.

This is the code that is running at onCreate:

mTitle = mDrawerTitle = getTitle();
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
        R.drawable.ic_drawer, R.string.navigation_drawer_open,
        R.string.navigation_drawer_close) {

    public void onDrawerClosed(View view) {
        super.onDrawerClosed(view);
        getActionBar().setTitle(mTitle);
    }

    public void onDrawerOpened(View drawerView) {
        super.onDrawerOpened(drawerView);
        getActionBar().setTitle(mDrawerTitle);
        dList = (ListView) findViewById(R.id.left_drawer);

        // My work-around to not get force close if item isn't visible
        if (itemOne > -1) {
            int visibleChildCount = (dList.getLastVisiblePosition() - dList
                    .getFirstVisiblePosition()) + 1;
            if (visibleChildCount > itemOne)
                dList.getChildAt(itemOne).setBackgroundColor(
                        Color.parseColor("#d3d3d3"));
        }
        if (itemTwo > -1) {
            int visibleChildCount = (dList.getLastVisiblePosition() - dList
                    .getFirstVisiblePosition()) + 1;
            if (visibleChildCount > itemTwo)
                dList.getChildAt(itemTwo).setBackgroundColor(
                        Color.parseColor("#33B5E5"));
        }

    }
};


    mDrawerLayout.setDrawerListener(mDrawerToggle);
    getActionBar().setDisplayHomeAsUpEnabled(true);
    getActionBar().setHomeButtonEnabled(true);

And the rest of the code:

dLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
dList = (ListView) findViewById(R.id.left_drawer);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myArrayOfItems);
dList.setAdapter(adapter);

dList.setOnItemClickListener(new OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> arg0, View v, int position, long id) {
        dList.setItemChecked(position, true);
        dLayout.closeDrawers();
        stateChanged = true;

            // Non-relevant action when item is clicked

    }
});

Finally, the activity XML:

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
<FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

<RelativeLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerHorizontal="true"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

     <!-- Items in my main activity -->

</RelativeLayout>

<ListView android:id="@+id/left_drawer"
    android:layout_width="240dp"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:choiceMode="singleChoice"
    android:divider="@android:color/transparent"
    android:dividerHeight="0dp"
    android:background="#fff"/>

</android.support.v4.widget.DrawerLayout>

Just let me know if you need any additional information. I'm sorry if I've missed the answer in anyone of the other questions or if this is a stupid question. I've tried searching for this a while now, but since I'm a beginner I haven't quite managed to figure out how to do this, so if anyone could help me with this I'd really appreciate it!

Edit :

Thanks to darnmason I finally got it to work. If anyone is interested, the code above will work with the following changes.

Change the line adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, myArrayOfItems); to this:

adapter = new NavigationAdapter(this, android.R.layout.simple_list_item_1, myArrayOfItems);

Then create the class NavigationAdapter.java with the following content (and make sure the integers itemOne/itemTwo are reachable from the main activity in some way):

import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;

public class NavigationAdapter extends ArrayAdapter<String> {

    // Create constructor matching super
    public NavigationAdapter(Context context, int resource, String[] objects) {
        super(context, resource, objects);
    }

    // Override method so that you can modify the view
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = super.getView(position, convertView, parent);
        // modify your view depending on position
        if (position == itemOne) {
                boolean positionOne = true;
            view.setBackgroundColor(positionOne ? Color.parseColor("#d3d3d3") : Color.WHITE);
        } else if (position == itemTwo) {
                boolean positionTwo = true;
            view.setBackgroundColor(positionTwo ? Color.parseColor("#33B5E5") : Color.WHITE);
        } else
            view.setBackgroundColor(Color.WHITE);

        return view;
    }
}

Also, make sure you delete the following lines from my previous code:

        // My work-around to not get force close if item isn't visible
        if (itemOne > -1) {
            int visibleChildCount = (dList.getLastVisiblePosition() - dList
                    .getFirstVisiblePosition()) + 1;
            if (visibleChildCount > itemOne)
                dList.getChildAt(itemOne).setBackgroundColor(
                        Color.parseColor("#d3d3d3"));
        }
        if (itemTwo > -1) {
            int visibleChildCount = (dList.getLastVisiblePosition() - dList
                    .getFirstVisiblePosition()) + 1;
            if (visibleChildCount > itemTwo)
                dList.getChildAt(itemTwo).setBackgroundColor(
                        Color.parseColor("#33B5E5"));
        }

The rendering of each item in the list should be handled in the adapter. What you should really do is extend ArrayAdapter and override getView to be something like this:

View view = super.getView(position, convertView, parent);
boolean selected = position matches any of your indexes;
view.setBackgroundColor(selected ? selectedColor : normalColor);
return view;

Another way to set the background is to use a state list drawable with selected state and default state. Then you just need to call view.setSelected(selected)

Edit: Extending ArrayAdapter

Create a new file, I like to create a new package to group all custom adapters under something like com.domain.name.adapters for example.

// Create new class extending ArrayAdapter<String>
public class NavigationAdapter extends ArrayAdapter<String> {

    // Create constructor matching super
    public NavigationAdapter(Context context, int resource, String[] objects) {
        super(context, resource, objects);
    }

    // Override method so that you can modify the view
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = super.getView(position, convertView, parent);
        // modify your view depending on position
        return view;
    }
}

Then just substitute NavigationAdapter for ArrayAdapter in your code. You may have to expose methods to set the selected indexes on the NavigationAdapter unless you have them in some glabally accessible location.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM