简体   繁体   English

不同的列表视图项目的不同选择颜色

[英]Different select colors for different list view items

I have the following requirements: 我有以下要求:

  • different colors for different list view items 不同的列表视图项目的不同颜色
  • colors are specified dynamically in the code 颜色在代码中动态指定
  • color should only be shown if the list view item is pressed/selected 只有在按下/选择列表视图项时才应显示颜色
  • color of the list view item should not change permanently 列表视图项的颜色不应永久更改

For whatever reasons it seems not to be as straight forward as I thought. 无论出于何种原因,它似乎都没有像我想象的那样直截了当。 The only solution that goes at least a little bit in the right direction is this one: https://stackoverflow.com/a/16978159/658718 唯一能够朝着正确方向发展的解决方案是: https//stackoverflow.com/a/16978159/658718

The caveat is, that this does not change the on select color, but changes the background color permanently, plus it already changes the background color for list view items if you scroll down a bit. 需要注意的是,这不会更改选择颜色,但会永久更改背景颜色,如果向下滚动,它已经更改了列表视图项的背景颜色。

How can I approach this? 我怎么处理这个?

I'd say go with state-aware drawables . 我会说具有状态意识的drawables Create a state-aware drawable XML file for each of the colors you want the background of your single ListView to be. 为您想要单个ListView背景的每种颜色创建一个状态感知的可绘制XML文件。 Here is an example of state- aware Drawables called background_black.xml and background_green.xml. 以下是一个名为background_black.xml和background_green.xml的状态感知Drawable的示例。 They makes your default background color white and while pressed/selected temporarily changes it to black or green. 它们使您的默认背景颜色为白色,而按下/选择时会暂时将其更改为黑色或绿色。 Both of these files go in your Drawable folder. 这两个文件都放在Drawable文件夹中。

background_black.xml background_black.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_window_focused="false" android:drawable="@android:color/white" />
    <item android:state_pressed="true" android:color="@android:color/black" />
    <item android:state_selected="true" android:color="@android:color/black" />
</selector>

background_green.xml background_green.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_window_focused="false" android:drawable="@android:color/white" />
    <item android:state_pressed="true" android:color="@android:color/green" />
    <item android:state_selected="true" android:color="@android:color/green" />
</selector>

In your ListView item xml file, assign an ID to your root layout or whichever element is providing the visible background color. 在ListView项目xml文件中,为根布局或任何提供可见背景颜色的元素指定ID。 For this example, Ill assume its your root layout. 对于此示例,Ill假设它是您的根布局。 Then in your Adapter's getView(), grab the element that you've assigned the id to and set one of the drawables you created with the color you want for its background. 然后在您的Adapter的getView()中,抓取您已分配id的元素,并使用您想要的背景颜色设置您创建的一个drawable。 Like so: 像这样:

@Override
public View getView(int position, View convertView, ViewGroup parent){
     //inflate your convertView, etc...
     ...
     ViewGroup baseLayout = (ViewGroup)convertView.findViewById(R.id.<your base layout id>);

     //these conditions need to reflect how you decide which list item gets which color
     if(position % 2 == 0){
         baseLayout.setBackground(R.drawable.background_black);
     } else {
         baseLayout.setBackground(R.drawable.background_green);
     //do whatever else you need
     ...
     return convertView;
}

NOTE: setBackground() is a new function, use setBackgroundDrawable() if coding for older versions of Android 注意:setBackground()是一个新函数,如果编码旧版Android,请使用setBackgroundDrawable()

The difficulty here is that pressed/checked color is dynamic. 这里的困难是按下/检查的颜色是动态的。 You cannot use static xml color-state-list. 您不能使用静态xml color-state-list。 But you can create ColorStateList by code. 但您可以通过代码创建ColorStateList Here is how to do that. 这是怎么做的。

You just have to implement the ListAdapter : 您只需要实现ListAdapter:

private class MyListAdapter implements ListAdapter{

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView!=null){
             CheckedTextView textView = (CheckedTextView)convertView;
             textView.setText("the text for item "+position);
             textView.setTextColor(makeColorStateListForItem(position));
             return textView;
        }else{
             CheckedTextView textView = new CheckedTextView(parent.getContext());
             textView.setText("the text for item "+position);
             textView.setTextColor(makeColorStateListForItem(position));
             return textView;
        }
    }

    private ColorStateList makeColorStateListForItem(int position){
        int pressedColor = pressedColorForItem(position);
        int checkedColor = checkedColorForItem(position);
        int defaultColor = defaultColorForItem(position);
        ColorStateList colorStateList = new ColorStateList(
                new int[][]{
                        new int[]{android.R.attr.state_pressed},
                        new int[]{android.R.attr.state_checked},
                        new int[]{0},
                },
                new int[]{
                        pressedColor, //use when state is pressed
                        checkedColor, //use when state is checked, but not pressed
                        defaultColor}); //used when state is not pressed, nor checked 
    }

    private int pressedColorForItem(int position){
        //write your business logic to determine color here
        return ...;
    }

    private int checkedColorForItem(int position){
        //write your business logic to determine color here
        return ...;
    }

    private int defaultColorForItem(int position){
        return Color.WHITE;
    }

    //all other adapter methods
    //...

Note the use of android.R.attr.state_checked instead of the more intuitive android.R.attr.state_selected because the state_selected is not very precisely define with a touch screen (ie state_selected can give the expected behavior on the emulator, but on a real device it will probably fail) 注意使用android.R.attr.state_checked而不是更直观的android.R.attr.state_selected因为state_selected不是用触摸屏非常精确地定义的(即state_selected可以在模拟器上给出预期的行为,但是在真正的设备,它可能会失败)

On the other hand state_checked + CheckedTextView is going to work correctly on both simulator and real device. 另一方面, state_checked + CheckedTextView将在模拟器和真实设备上正常工作。

Just call List.setChoiceMode(...); 只需调用List.setChoiceMode(...); when initializing listView and ListView.setItemChecked in the clickListener. 在clickListener中初始化listView和ListView.setItemChecked时。

final ListView listView = ...
listView.setAdapter(new MyListAdapter());
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        listView.setItemChecked(position,true);
    }
});

EDIT : to change the item background : just create a StateListDrawable instead of a simple ColorStateList : 编辑 :更改项目背景:只需创建一个StateListDrawable而不是简单的ColorStateList:

private Drawable makeBackgroungForItem(int position){
    int pressedColor = pressedBackgroundColorForItem(position);
    int checkedColor = checkedBackgroundColorForItem(position);
    int defaultColor = defaultBackgroundColorForItem(position);
    StateListDrawable stateListDrawable = new StateListDrawable();
    stateListDrawable.addState(new int[]{android.R.attr.state_list_pressed}, new ColorDrawable(pressedColor));
    stateListDrawable.addState(new int[]{android.R.attr.state_list_checked}, new ColorDrawable(checkedColor));
    stateListDrawable.addState(new int[]{0, new ColorDrawable(defaultColor));
    return stateListDrawable;
}

And in the getView(...) getView(...)

textView.setBackground(makeBackgroungForItem(position));

I suggest the following approach: 我建议采用以下方法:

  • You need to create a ListView Adapter that supports multiple different items 您需要创建一个支持多个不同项目ListView Adapter
  • Each different Item class represents a different color and can have it's own implementation of how the pressed or selected state should be handled 每个不同的Item类表示不同的颜色,并且可以拥有它应该如何处理按下或选择状态的自己的实现
  • Since every ListItem will have it's own .xml layout file, you can specify the desired selector there 由于每个ListItem都有自己的.xml布局文件,因此您可以在那里指定所需的选择器

What you need: 你需要什么:

  • A baseclass ListItem that every item in the ListView inherits from ListView中每个项继承自的基类 ListItem
  • This class provides abstract methods to get the View that represents the item and it's type 此类提供抽象方法来获取表示项目及其类型的View
  • If required, the ListItem class could have an Integer field mColor, that holds the color the item represents 如果需要, ListItem类可以有一个Integer字段mColor,它保存项表示的颜色
  • If required, the ListItem class could have a method to setup the selector with the speficied color 如果需要, ListItem类可以有一个方法来设置具有特定颜色的选择器

Example: 例:

public abstract class ListItem {

    public static final int TYPE_WHATEVER_1                     = 0;
    public static final int TYPE_WHATEVER_2                     = 1;
    // and so on...

    /** the total number of list-item-types */
    public static final int TYPE_COUNT              = typecounthere;

    // if required for your implementation:
    protected int mColor;

    public abstract int getViewType();
    public abstract View getView(LayoutInflater inflater, View convertView);

    /** creates and sets the selector with your specified color */
    public void setupSelectorColor() {

        StateListDrawable states = new StateListDrawable();

        ColorDrawable cdPressed = new ColorDrawable(mColor);
        ColorDrawable cdSelected = new ColorDrawable(mColor);
        ColorDrawable cdDefault = new ColorDrawable(Color.TRANSPARENT);

        states.addState(new int[] {
                android.R.attr.state_pressed
        },
                cdPressed);
        states.addState(new int[] { 
                android.R.attr.state_selected
        },
                cdSelected);
        states.addState(new int[] {},
                cdDefault);

        setBackgroundDrawable(states);
    }
}
  • For each color, create a subclass of ListItem 对于每种颜色,创建ListItem的子类
  • Inside the getView(...) method, inflate your desired layout getView(...)方法内部,膨胀您想要的布局
  • Don't forget to return the correct type in the getViewType() method 不要忘记在getViewType()方法中返回正确的类型
  • Do whatever you want with your color in here 在这里用你的颜色做任何你想做的事
  • Setup the selector with your color 用您的颜色设置选择器

Example: 例:

public class ItemTypeOne extends ListItem {

    public ItemTypeOne(int color) {
        mColor = color;
    }

    @Override
    public int getViewType() {
            // return the type
        return TYPE_WHATEVER_1;
    }

    @Override
    public View getView(LayoutInflater inflater, View convertView) {

        if(convertView == null) {
            // inflate the layout 
            convertView = inflater.inflate(R.layout.item_type_one, null);
        }    

        // setup the selector
        setupSelectorColor();

        // do other stuff

        return convertView;
    }
}
  • An adapter that supports different item types 支持不同项类型的适配器

Example: 例:

public class ListItemAdapter extends ArrayAdapter<ListItem> {

    public ListItemAdapter(Context context, List<ListItem> objects) {
        super(context, 0, objects);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        return getItem(position).getView(LayoutInflater.from(getContext()), convertView);
    }

    @Override
    public int getItemViewType(int position) {
        return getItem(position).getViewType();
    }

    @Override
    public int getViewTypeCount() {
        return ListItem.TYPE_COUNT;
    }
}

Putting it all together: 把它们放在一起:

ArrayList<ListItem> list = new ArrayList<ListItem>();
// fill the list
list.add(new ItemTypeOne(somecolor));
list.add(new ItemTypeTwo(somecolor));
list.add(new ItemTypeOne(somecolor));
list.add(new ItemTypeWhatever(somecolor));

ListView lv = (ListView) v.findViewById(R.id.listView1);

ListItemAdapter a = new ListItemAdapter(Context, list);         
lv.setAdapter(a);

Concerning CustomViews and selectors and their behaviour , I suggest reading this question (and answer): How to implement a CustomView with custom selector states? 关于CustomViews选择器及其行为 ,我建议阅读这个问题(和答案): 如何使用自定义选择器状态实现CustomView?

I'd say don't overcomplicate this. 我想说不要过于复杂。 It can be simple as create an array of int that will contain possible colors, and set them to each item using Random class. 它可以很简单,因为创建一个包含可能颜色的int数组,并使用Random类将它们设置为每个项目。

// This goes inside hosting fragment or activity
listview.setOnItemClickListner( new OnItemClickListener() {
      @Override
      public void onItemClick(AdapterView parent, View view, int position, long id) {
           if(view.isSelected()){
               view.setSelected(false);
               // also maybe change bg color back to normal?
           }

           else {
                // This one for always a different color
                view.setBackgroundColor(adapter.getColor());

                // This is for foreground color change instead of background
                FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.my_frame_layout);

                final Drawable drawable = new ColorDrawable( /*  your getColor() function  */ );
                frameLayout.setForeground(drawable);

                // This one for alwyas the same color for the row at position given by {@param position}
                view.setBackgroundColor(adapter.getColor(position));
                view.setSelected(true);
           }
      }
});



// All this goes inside your custom listview Adapter
int[] colors = {
      R.colors.red,
      R.colors.blue,
      ...
}

Random random = new Random();

// If each time the selection will bring a different color, use this implementation
public int getColor() {
    return colors[random.nextInt(colors.length)]; 
}

// If each row should have different color, but always the same color for a row then use this one instead
SparseIntArray spa = new SparseIntArray();
public int getColor(int position) {
     if(spa.get(position) == 0) {
         // the color hasnt been created for that row yet
         spa.put(position, colors[random.nextInt(colors.length)];
     }

     return spa.get(position);     
}

**Edit: ** now, if what you want is a foreground selection, then your row should have a FrameLayout container, and you should change it's 'android:foreground' property: **编辑:**现在,如果您想要的是前景选择,那么您的行应该有一个FrameLayout容器,您应该更改它的'android:foreground'属性:

final Drawable drawable = new ColorDrawable( /*  your getColor() function  */ );
frameLayout.setForeground(drawable);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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