【问题标题】:Different select colors for different list view items不同列表视图项的不同选择颜色
【发布时间】:2014-06-18 12:07:52
【问题描述】:

我有以下要求:

  • 不同列表视图项的不同颜色
  • 颜色在代码中动态指定
  • 只有在按下/选择列表视图项时才会显示颜色
  • 列表视图项的颜色不应永久更改

无论出于何种原因,它似乎并不像我想象的那样直截了当。唯一至少朝着正确方向发展的解决方案是这个:https://stackoverflow.com/a/16978159/658718

需要注意的是,这不会改变选择时的颜色,而是永久改变背景颜色,而且如果你向下滚动一点,它已经改变了列表视图项的背景颜色。

我该如何处理?

【问题讨论】:

  • Mantain falg for color like integer(red : 1, blue : 2 .etc..)...将该值存储在您使用过的适配器或数组中,在项目中单击获取该值。 . 取决于该值显示颜色.. 简单

标签: java android listview user-interface


【解决方案1】:

这里的困难在于按下/选中的颜色是动态的。您不能使用静态 xml 颜色状态列表。但是您可以通过代码创建ColorStateList。这是如何做到这一点的。

你只需要实现 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
    //...

注意使用android.R.attr.state_checked而不是更直观的android.R.attr.state_selected,因为state_selected不是用触摸屏非常精确地定义(即state_selected可以在模拟器上给出预期的行为,但在真实设备上它可能会失败)

另一方面,state_checked + CheckedTextView 将在模拟器和真实设备上正常工作。

在初始化listView时调用List.setChoiceMode(...);,在clickListener中调用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);
    }
});

编辑:更改项目背景:只需创建一个 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;
}

而在getView(...)

textView.setBackground(makeBackgroungForItem(position));

【讨论】:

  • 这看起来是迄今为止最有希望的。刚看完,为什么文字颜色设置在getView开头而不是setBackground?
  • 也不是 100% 确定 ListAdapter,因为我使用的是 ArrayAdapter&lt;T&gt;,但可能或多或少相同
  • 关于适配器:只是扩展了 ArrayAdapter(它会节省你编写一些方法的实现)。关于背景:此处的示例代码更改了文本颜色。可以为背景构建一个非常相似的解决方案(我将编辑我的帖子以说明如何更改背景颜色)
  • @Mahoni 我刚刚编辑了我的帖子,详细介绍了如何使用动态状态列表更改背景
  • 工作就像一个魅力,非常感谢你的彻底
【解决方案2】:

我会说选择state-aware drawables。为您希望单个 ListView 作为背景的每种颜色创建一个可感知状态的可绘制 XML 文件。下面是一个名为 background_black.xml 和 background_green.xml 的状态感知 Drawable 示例。它们使您的默认背景颜色为白色,并在按下/选择时将其临时更改为黑色或绿色。这两个文件都在您的 Drawable 文件夹中。

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

<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>

在您的 ListView 项目 xml 文件中,为您的根布局或提供可见背景颜色的任何元素分配一个 ID。对于这个例子,我假设它是你的根布局。然后在您的适配器的 getView() 中,获取您已分配 id 的元素,并使用您想要的背景颜色设置您创建的可绘制对象之一。像这样:

@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;
}

注意:setBackground() 是一个新函数,如果为旧版本的 Android 编码,请使用 setBackgroundDrawable()

【讨论】:

  • 我希望你没有回答这个问题。因为我知道这是正确的答案,我会自己回答以获得赏金。那好吧。干得好!
  • @notrix 不错,但是我必须使用什么条件才能在按钮按下时显示我的自定义颜色?
  • 这完全取决于你。我想这取决于您希望以某种颜色拥有哪些列表项。我会更新答案,使所有偶数项都有黑色背景,所有奇数项都有其他背景。
【解决方案3】:

我建议以下方法:

  • 您需要创建一个支持多个不同项目的ListView Adapter
  • 每个不同的 Item 类代表不同的颜色,并且可以有自己的实现来处理按下或选中状态的处理方式
  • 由于每个ListItem 都有自己的 .xml 布局文件,您可以在那里指定所需的选择器

你需要什么:

  • 一个基类 ListItem,ListView 中的每个项目都继承自
  • 该类提供抽象方法来获取代表项目及其类型的View
  • 如果需要,ListItem 类可以有一个 Integer 字段 mColor,用于保存项目所代表的颜色
  • 如果需要,ListItem 类可以有一个方法来设置具有指定颜色的选择器

例子:

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);
    }
}
  • 为每种颜色创建ListItem 的子类
  • 在 getView(...) 方法中,扩展您想要的布局
  • 不要忘记在getViewType() 方法中返回正确的类型
  • 在这里用你的颜色做任何你想做的事
  • 使用您的颜色设置选择器

例子:

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;
    }
}
  • 支持不同项目类型的适配器

例子:

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;
    }
}

把它们放在一起:

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);

关于CustomViews 和选择器及其行为,我建议阅读这个问题(和答案):How to implement a CustomView with custom selector states?

【讨论】:

    【解决方案4】:

    我会说不要过于复杂。它可以很简单,例如创建一个包含可能颜色的 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);     
    }
    

    **编辑:** 现在,如果你想要的是前景选择,那么你的行应该有一个 FrameLayout 容器,你应该改变它的 'android:foreground' 属性:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-07
      • 1970-01-01
      • 2014-05-15
      • 2012-06-13
      • 1970-01-01
      • 2010-10-28
      • 2014-08-02
      • 1970-01-01
      相关资源
      最近更新 更多