【问题标题】:How can I change the background color for an specific row properly in a ListView? (Android)如何在 ListView 中正确更改特定行的背景颜色? (安卓)
【发布时间】:2011-06-05 19:24:27
【问题描述】:

我花了几天时间试图解决我在 Android 上使用 ListViews 时遇到的问题。我想使用 ListView 实现单个选择列表框。因此,我希望只有一行具有预定义的浅色背景颜色,而其余行具有另一种预选颜色。我遇到的问题是,当我单击特定行时,另一行突出显示,而不是我按下的行。我添加了几条消息来记录正在发生的事情,但似乎一切正常。这是我的代码:

public class TryListViewActivity extends Activity {
    protected static final int NO_SELECTED_COLOR = 0xFF191919;
    protected static final int SELECTED_COLOR = 0xFF3366CC;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ListView listView = new ListView(this);
        ArrayList<String> list = new ArrayList<String>();
        list.add("Option 1");
        list.add("Option 2");
        list.add("Option 3");
        list.add("Option 4");
        list.add("Option 5");
        list.add("Option 6");
        list.add("Option 7");
        list.add("Option 8");
        list.add("Option 9");
        list.add("Option 10");
        list.add("Option 11");
        list.add("Option 12");
        list.add("Option 13");
        list.add("Option 14");
        list.add("Option 15");

        ArrayAdapter<String> listAdapter = new ArrayAdapter<String>(
            this,
            R.layout.list_box_entry,
            list
        );
        listView.setAdapter(listAdapter);

        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

        // Set the listener
        listView.setOnItemClickListener(
            new AdapterView.OnItemClickListener() {
                public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] Item clicked: position="
                        + position + ";id=" + id
                    );

                    // First, set all rows to be unselected
                    int counter  = parent.getCount();
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] "
                        + counter + " items found inside the parent"
                    );
                    int children = parent.getChildCount();
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] "
                        + children + " views found inside the parent"
                    );
                    for(int i=0;i<children;i++) {
                        Log.i(
                            "Log",
                            "[SingleSelectionListBox] Child "
                            + i + " has message "
                            + ((TextView)parent.getChildAt(i)).getText()
                        );
                    }

                    // Too inefficient but for now is OK
                    for(int i=0;i<children;i++)
                        parent.getChildAt(i)
                            .setBackgroundColor(NO_SELECTED_COLOR);
                    Log.i("Log",
                        "[SingleSelectionListBox] First visible position: "
                        + parent.getFirstVisiblePosition()
                    );

                    // Set the background color
                    TextView textView = (TextView)(parent.getChildAt(
                        position-parent.getFirstVisiblePosition()));
                    textView.setBackgroundColor(SELECTED_COLOR);
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] Text inside the "
                        + " View changing the color " + textView.getText()
                    );
                }
            }
        );
        setContentView(listView);
    }
}

在资源 (res/layout) 中,我插入了一个名为 list_text_entry.xml 的文件,其中包含以下内容

<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
  android:layout_height="fill_parent" android:gravity="center"
  android:textColor="#FFFFFF" android:padding="10dp" android:textSize="16sp">
</TextView>

例如,如果我在 listView 之前向下滚动时单击“选项 11”条目,直到我看到的第一行是“选项 4”,则“选项 7”行显示为选中的唯一一个携带背景中的蓝色。有人可以解释一下我这里发生了什么吗?我在收到的消息下方发布了日志。

[SingleSelectionListBox] Item clicked: position=10;id=10
[SingleSelectionListBox] 15 items found inside the parent
[SingleSelectionListBox] 11 views found inside the parent
[SingleSelectionListBox] Child 0 has message Option 4
[SingleSelectionListBox] Child 1 has message Option 5
[SingleSelectionListBox] Child 2 has message Option 6
[SingleSelectionListBox] Child 3 has message Option 7
[SingleSelectionListBox] Child 4 has message Option 8
[SingleSelectionListBox] Child 5 has message Option 9
[SingleSelectionListBox] Child 6 has message Option 10
[SingleSelectionListBox] Child 7 has message Option 11
[SingleSelectionListBox] Child 8 has message Option 12
[SingleSelectionListBox] Child 9 has message Option 13
[SingleSelectionListBox] Child 10 has message Option 14
[SingleSelectionListBox] First visible position: 3
[SingleSelectionListBox] Text inside the View changing the color Option 11

我可以猜到 ViewGroup 中的所有子项都是按从上到下的顺序排列的,即使我在代码中这样做:

TextView textView = (TextView)(parent.getChildAt(
    position-parent.getFirstVisiblePosition()
));
textView.setBackgroundColor(SELECTED_COLOR);

出现消息Option 11,但实际上是选中的option 7。这是 Android 的错误吗?

【问题讨论】:

  • 下次请使用四个空格来标记代码,而不是使用 HTML 标记。有关详细信息,请参阅Markdown Editing Help 页面。

标签: android listview background-color


【解决方案1】:

我相信这是由于 ListViews 编号和重用行的方式。因此,与其使用parent.getChildAt(i) 来获取您想要操作的行,不如使用传递给onItemClick 本身的View 对象。

view.setBackgroundColor(SELECTED_COLOR);

【讨论】:

  • 你好 Dave,很遗憾说我之前尝试过,直接使用函数中提供的 View 的结果是完全一样的。出于这个原因,我试图找到一种更复杂的替代方法,但效果不佳。不过谢谢你的回答!
【解决方案2】:

是的,你应该像戴夫所说的那样使用:

view.setBackgroundColor(SELECTED_COLOR);

也许

view.refreshDrawableState(); 

但是,由于 Android 回收列表,它会在屏幕上未显示的每个第一个项目上重复您选择的颜色。因此,如果您的屏幕尺寸可以显示十个项目而不是第 11 个,那么当您滚动时,第 21 个等也会显示为选中状态。

为避免这种情况,您必须创建一个自定义适配器。然后在 getView 中你需要这样说:

if (myActivity.selectedRow != position){
    v.setBackgroundColor(Color.TRANSPARENT);
} else {
    v.setBackgroundColor(SELECTED_COLOUR);
}

其中 selectedRow 是 myActivity 中的 public static int selectedRow,创建您的列表的活动。在那里您存储单击列表时选择的行号。

【讨论】:

    【解决方案3】:

    感谢 Dave 和 ChristianB 的回答。我仍然不知道为什么Android会这样做,所以这个问题仍然没有解决。但是,我找到了一种方法来获得创建自定义适配器所需的东西。我会给你代码,以防有人需要。

    public class CustomAdapter extends ArrayAdapter<String> {
    
        protected static final int NO_SELECTED_COLOR = 0xFF191919;
        protected static final int SELECTED_COLOR = 0xFF3366CC;
    
        private ArrayList<String> items;
        private LayoutInflater mInflater;
        private int viewResourceId;
        private int selectedPosition;
    
        public CustomAdapter(Activity activity,int resourceId,
            ArrayList<String> list) {
            super(activity,resourceId,list);
    
            // Sets the layout inflater
            mInflater = (LayoutInflater)activity
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    
            // Set a copy of the layout to inflate
            viewResourceId = resourceId;
    
            // Set a copy of the list
            items = list;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            TextView tv = (TextView)convertView;
            if (tv == null) {
                tv = (TextView)mInflater.inflate(viewResourceId, null);
            }
            tv.setText(items.get(position));
    
            // Change the background color
            if (position==selectedPosition) tv.setBackgroundColor(SELECTED_COLOR);
            else tv.setBackgroundColor(NO_SELECTED_COLOR);
    
            return tv;
        }
    
        public void setSelected(int position) {
            selectedPosition = position;
        }
    }
    

    所以,在 ListView 初始化中我只需要放置这个监听器:

    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View view,
            int position, long id) {
            ((CustomAdapter)listAdapter).setSelected(position);
            listView.invalidate();
        }
    });
    

    这不是一个有效的解决方案,因为我是从 ArrayAdapter 扩展而来的,它假设提供了所有数据的副本。因此,我使用的内存比需要的多得多,如果列表变得非常大,我可能会遇到内存问题。所以,如果有人知道更好的解决方案,请发布!

    【讨论】:

    • 膨胀每个视图是解决这个问题的好方法。如果您担心内存影响(除非您显示 1,000 个 String,这不是一个大问题),您可能希望改用 CursorAdapter 并将您的数据存储在 SQLite 数据库中。
    【解决方案4】:

    listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {

      @Override
    public void onItemClick(AdapterView<?> parent, final View view, int position, long   id) 
    {
    
        View v;
    
        int count = parent.getChildCount();
        v =parent.getChildAt(position);
        parent.requestChildFocus(v, view);  v.setBackground(res.getDrawable(R.drawable.transparent_button));
    
                for (int i=0; i<count; i++)
                {
                    if (i!= position)
                    {
                        v = parent.getChildAt(i);t  v.setBackground(res.getDrawable(R.drawable.not_clicked));
    
                    }
                }
    
            }
    
        });
    

    基本上,创建两个可绘制对象 - 一个是透明的,另一个是所需的颜色。请求焦点在点击位置(定义的 int 位置)并更改所述行的颜色。然后遍历父列表视图,并相应地更改所有其他行。这说明了用户多次单击列表视图的情况。

    【讨论】:

      【解决方案5】:

      我用过这段代码,效果很好,你应该试试这个:

      listView.getChildAt(0).setBackgroundColor(Color.BLUE);
      

      【讨论】:

        【解决方案6】:
            @Override
            public View getView(int position, View convertView, ViewGroup parent) 
            {
           LayoutInflater inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        
          View itemView = inflater.inflate(R.layout.verselayout, parent, false); 
            txttitle = (TextView) itemView.findViewById(R.id.Versetxt);
            if (position%2 == 0) {
               txttitle.setTextColor(Color.parseColor("#FFFFFF"));
            }
         else
               {
            txttitle.setTextColor(Color.parseColor("#FFFF00"));
               }
        
             return itemView;
            }
        

        【讨论】:

          【解决方案7】:

          试试这个,

          OnItemClickListener onitemclick = new OnItemClickListener() {
          
                  @Override
                  public void onItemClick(AdapterView<?> adapter, View arg1, int position, long id) {
                          selectedItem= position;
                          adapter.notifyDataSetChanged();
                  }
              };
          

          覆盖适配器的 getView() 方法:

           @Override
              public View getView(int position, View convertView, ViewGroup parent) {
                  final View view = View.inflate(context, R.layout.item_list, null);
          
                  if (position == selectedItem) {
                      // set your color
                  }
          
                  return view;
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-05-17
            • 1970-01-01
            • 1970-01-01
            • 2013-11-22
            • 1970-01-01
            相关资源
            最近更新 更多