【问题标题】:Change attributes of view on list item selection更改列表项选择的视图属性
【发布时间】:2012-03-22 04:25:34
【问题描述】:

我有一个包含自定义行的 ListView。此自定义行包含以下 UI 元素

  1. ImageView imageView1,imageView2
  2. TextView textview1,textView2,textView3

要求是只要列表行被选择,就会有以下变化

imageView1 背景,颜色改变

imageView1 颜色改变

textview1 颜色、大小和字体发生变化

textview2 颜色,大小改变

textview3 颜色,大小改变

为此设计的最佳方式是什么?

AFAIK 我们无法在选择器中应用样式。有没有更好的方法来解决这个问题而不是在 java 代码中处理?

我们有 setOnItemSelectedListener 可以在 Listview 上设置,它有以下回调方法:

i) onItemSelected

ii) onNothingSelected

但是,没有回调方法可以提供失去焦点的项目的详细信息。这是进行更改的更好地方吗?

提前致谢。

【问题讨论】:

  • "AFAIK 我们无法在选择器中应用样式。有没有更好的方法来解决这个问题而不是在 java 代码中处理?"如果你不能在选择器中做到这一点,除了在 Java 中还能怎么做?也许您可以覆盖默认选择器引用的样式属性?
  • 可以应用样式来改变背景颜色/drawable。改变字体大小无济于事。我还需要为每个子视图指定选择器吗?在 java 代码中,在 getView() 中。

标签: android listview focus


【解决方案1】:

我认为您在这里要做的是创建一个Compound Control。您可以在您的 SDK 目录中的 samples//ApiDemos 子目录中找到一个示例(如果您没有这些目录,您可能需要下载 Android source)。

我要做的是创建一个类,该类继承自您用于自定义行的任何类型的布局。假设这个例子是LinearLayout。在您的子类构造函数中,您可以从资源中扩展您的布局,找到子视图并将它们附加到实例变量,然后将 LinearLayout 返回给调用者。

在这个子类中,您可以覆盖setSelected 并以您想要的任何方式操作子视图。

这是我根据您的帖子描述的示例:

public class MyCustomLayout extends LinearLayout {
    public ImageView imageView1;   // These are public since you likely want
    public ImageView imageView2;   // to set them in your Adapter.
    public TextView textView1;
    public TextView textView2;
    public TextView textView3;

    public MyCustomLayout(Context ctxt) {
        super(ctxt);

        // The call below attaches the items in the mycustomlayout.xml file to this
        // instance, which serves as the root element in the layout.
        LayoutInflater.from(ctxt).inflate(R.layout.mycustomlayout, this, true);

        imageView1 = (ImageView)findViewById(R.id.imageview1);
        imageView2 = (ImageView)findViewById(R.id.imageview2);
        textView1 = (TextView)findViewById(R.id.textview1);
        textView2 = (TextView)findViewById(R.id.textview2);
        textView3 = (TextView)findViewById(R.id.textview3);
    }

    @Override
    public void setSelected(boolean selected) {
        super.setChecked(selected);
        if (selected) {
            imageView1.setBackgroundColor(0xFFFF0000); // whatever color you want
            // Do all the rest of the setting of your subviews here for
            // the case when the row is selected.
        } else {
            imageView1.setBackgroundColor(0xFF000000); // whatever color you want
            // Do all the rest of the setting of your subviews here for
            // the case when the row is not selected
        }
    }
}

现在,在 mycustomlayout.xml 文件中,您要使用 <merge> 标记,这样您就不会创建不需要的布局:

<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <ImageView
        android:id="@+id/imageview1" />
    <ImageView
        android:id="@+id/imageview2" />
    <TextView
        android:id="@+id/textview1" />
    <TextView
        android:id="@+id/textview2" />
    <TextView
        android:id="@+id/textview3" />
</merge>

显然,我已经省略了上面子视图的所有配置,但是您应该了解如何设置 XML 文件。如果您不想与 XML 和 LayoutInflater 混淆,也可以在代码中执行此操作。您可以查看 Android 开发博客上的 this helpful blog post,该博客最后讨论了一个类似案例。

然后,在您的适配器的getView 中,您可以创建(或回收)MyCustomLayout 的实例,设置它们,并让框架负责跟踪哪些行被选中,哪些未被选中。

【讨论】:

    【解决方案2】:

    我有一个粗略的方法……但它的过程非常繁重…… 一旦选择了一个项目,刷新您的列表视图...仅对于所选项目,它应该显示特定字体等...并将所选项目的 id 存储在某个变量中以匹配它,同时填充适配器中的视图...

    【讨论】:

      【解决方案3】:

      在活动级别获取一个静态整数变量selectedPos

      在 onItemSelected 中设置该变量 selectedPos = position 并在 onNothingSelected 中将其设置为 -1 并为您的适配器对象调用 notifyDataSetChanged()。

      现在在 ListView 的适配器类中。 在getView中写条件为

      if(position == selectedPos)
      {
         // set color and background to view
      }
      

      并且不要忘记在适配器类中编写这两个方法,

      @Override
      public int getItemViewType(int position) {
       return position;
      }
      
      @Override
      public int getViewTypeCount() {
       return _ArrayListObject.size();
      }
      

      【讨论】:

      • 谢谢索尼。但是,这意味着要为每个选定的项目重新绘制 listView。可以以任何方式避免这种情况吗?当“基础数据已更改”时,也应该调用 notifyDataSetChanged() ,这不是这里的情况。框架不支持处理这种情况吗?
      • @khotmanish nope.. :) :( .. 我脑子里只有这个想法,我认为这对你有用..
      【解决方案4】:

      霍特曼语,

      我不确定您所说的最有效方式是什么意思。有许多方法非常快速,但这通常取决于您提前知道什么以及您不知道什么。还必须理解,动态视图属性被严重误解,但尽管有流行的神话,但它的效率很高。 Google 的人员在 UI 开发方面做得非常出色。

      如果您提前知道它,您可以简单地为每个视图创建一个 StateListDrawable,并在您运行适配器时将其应用于每个视图。

      要构建您的 StateListDrawable,请创建一个:

      StateListDrawable _drawStates;
      

      然后,为每个状态添加你需要的drawable:

      _drawStates.add(new int[] {android.R.attr.state_pressed}, _newImage);
      

      这就像您的适配器的 getView() 方法中的以下行一样简单:

      v.setBackgroundDrawable(_drawStates);
      

      或者,您可以根据选择动态更改背景。这可以在OnListItemClick() 中完成。你看,OnListItemClick() 会给你被点击的确切视图。一旦你有了这个,你可以通过它的id找到孩子并改变它的属性和invalidate()它。我经常使用这个技巧。为我节省了大量内存,但我并没有做任何真正占用处理器的事情。您可以在此处更改几乎任何属性,而无需进行任何处理。这是一个例子:

      @Override protected void onListItemClick(final ListView inList, final View v, final int atPos, final long rowID)
      {//Get the Child, set the properties
          ImageView _img = (ImageView)(v.findViewById(R.id.app_package));
          _img.setBackgroundColor(myClickedColor);
          _img.invalidate();  // <---- BEST Option
      // ... OR ...
          v.invalidate();     // <---- 2nd Best Option
      // ... OR ...
          inList.invalidate();  // <---- Last resort or specialized implementation
      }
      

      请务必注意,动态执行此操作在背景颜色方面有一些注意事项。您必须从 Clicked View 中获取孩子...而不是 ListView。其次,在更改背景颜色时,实际上是在更改背景 Drawable。这意味着所有其他“有状态”属性都会消失,您必须手动将其更改回来(如果您希望它开始)。

      现在,一种更流行但不太灵活的方法是用 XML 构建您的 StateListDrawable。 Android 开发者网站上有很多资源可以帮助您了解如何做到这一点。

      希望这会有所帮助, 模糊逻辑

      【讨论】:

      • 感谢您对模糊逻辑的回复。但是 StateListDrawable 只会改变颜色/drawables。更改字体大小不会有太大帮助。我也对 onSelect 状态而不是 onClicked 状态更感兴趣。 (我将使用轨迹球而不是 onTouch)。因此 onListItemClick 不会帮助我:(。无论如何感谢您的简短回答:)
      猜你喜欢
      • 1970-01-01
      • 2015-01-13
      • 1970-01-01
      • 1970-01-01
      • 2019-06-18
      • 1970-01-01
      • 2015-05-26
      • 1970-01-01
      • 2020-03-25
      相关资源
      最近更新 更多