V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
creatorYC
V2EX  ›  Android

android 中 imageview 设置可见性的问题

  •  
  •   creatorYC · 2015-08-25 17:38:51 +08:00 · 8600 次点击
    这是一个创建于 3166 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我用 SimpleAdapter 作为 ListView 的适配器,自定义了一个布局,里面有一个 ImageView 和一个 TextView ,我想要实现 ListView 的每一项都是文字加图片的效果。布局文件如下: <ImageView
    android:id="@+id/ItemImage"
    android:layout_alignParentRight="true"
    android:layout_marginRight="20sp"
    android:layout_width="30sp"
    android:layout_height="30sp"
    android:visibility="gone"
    />

    <TextView
        android:id="@+id/ItemText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20sp"
        android:textSize="20sp" />
     我设置了 android:visibility="gone",是想开始的时候让图片不显示,在需要显示的地方再显示。然后我点击某个按钮后,想让此图片显示。所以我先遍历 ListView ,然后得到这个 ImageView 对象,再设置它的可见性,代码如下( set 是一个按钮):
     set.setOnClickListener (new OnClickListener () {
            @Override
            public void onClick (View v ) {
                 //全选遍历 ListView 的选项,每个选项就相当于布局配置文件中的 RelativeLayout  
                 for (int i = 0; i < cityListView.getCount (); i++){  
                     RelativeLayout layout = 
                             (RelativeLayout ) cityListView.getAdapter ().getView (i, null, null );  
                     ImageView image = (ImageView ) layout.getChildAt (0 );
                     image.setImageResource (R.drawable.delete );
                     image.setVisibility (View.VISIBLE );
    
                 }
            }
        });
        可是都设置完成后,先前隐藏的图片还是都没显示,不知道什么愿因,
        ps :我的目的是让 ListView 里面被隐藏的图片显示出来,不知道是不是应该这样做
    
    36 条回复    2015-08-28 12:44:36 +08:00
    jimmy
        1
    jimmy  
       2015-08-25 18:16:00 +08:00
    执行了 adapter.notifyOnDataChange 了吗?
    xhuuanniqege
        2
    xhuuanniqege  
       2015-08-25 18:24:43 +08:00 via Android
    正常的做法是在 adapter 里面设置一个辅助数组保存 imageview 的状态,在 getview 方法中根据这个数组设定 imageview 的可见性。当当按下按钮后,改变状态数组的内容然后 notifydatachange 。你遍历每个 view 没用的,因为当 view 看不见时会被回收。
    ljbha007
        3
    ljbha007  
       2015-08-25 18:35:26 +08:00
    首先你这么写代码的习惯很不好 耦合性太高 万一布局一变 imageview 不是在第 0 个了 代码就失效了 应该像楼上说的这么写

    其次 千万不要手动调用 getView 方法
    如果你 getView 方法里根据 xml 渲染了一个 listView 里的一行的话 你再次调用还会再渲染一次 而且还会触发 listView 的 view 循环利用机制 非常浪费
    ssynhtn
        4
    ssynhtn  
       2015-08-25 19:13:39 +08:00 via Android
    难道是因为 textview android:layout_width="match_parent"覆盖了?
    不过不管是什么原因,你这写法都很不常规
    Bown
        5
    Bown  
       2015-08-25 19:17:48 +08:00
    cityListView.getAdapter ().getView (i, null, null ) 改成 cityListView.getChildAt (i )
    JayFang1993
        6
    JayFang1993  
       2015-08-25 19:19:14 +08:00
    一看就是新手,问问题的问法以及代码一看就像是来自百度知道的那种,拉低了 v 站的逼格~
    Bown
        7
    Bown  
       2015-08-25 19:20:59 +08:00
    没仔细看题。。 都有 view 重用,正确做法是 adapter 里面一个 list 记录每个 item 的状态是否 visible , getView 里面根据对应 index 的状态设置 visibility ,然后点击事件里面修改这个状态记录再 notifyDataSetChange
    allan1st
        8
    allan1st  
       2015-08-25 19:55:46 +08:00 via Android
    你这样写 ListView 还有什么意义,建议把 ListView 原理好好看几遍。另外,请尽量转用 RecyclerView 。
    creatorYC
        9
    creatorYC  
    OP
       2015-08-25 20:34:19 +08:00
    @JayFang1993 按您的说法,新手应该去哪呢?
    creatorYC
        10
    creatorYC  
    OP
       2015-08-25 20:36:16 +08:00
    @ssynhtn 我是新手,请原谅,这样写为什么不常规呢
    creatorYC
        11
    creatorYC  
    OP
       2015-08-25 20:40:32 +08:00
    @allan1st 抱歉,我是新手,我还不太懂代码的优化之类的,能不能详细说点
    creatorYC
        12
    creatorYC  
    OP
       2015-08-25 20:42:36 +08:00
    @ljbha007 多谢指导,我会好好看的,谢谢您
    anthonyeef
        13
    anthonyeef  
       2015-08-25 20:48:43 +08:00 via Android
    @xhuuanniqege 回答的很好啊也很耐心
    ssynhtn
        14
    ssynhtn  
       2015-08-25 20:50:01 +08:00 via Android
    @creatorYC 我不知道你是哪里学的,反正随便搜索 listview 的用法都不是这样的。
    因为 listview 里面的子 view 对象是会被回收重复利用的,所以就算这个写法一开始能正常工作,只要滑动几下,一个 view 滑出屏幕就会被回收,用到别的 position 去。
    入门推荐 udacity 的免费课程
    creatorYC
        15
    creatorYC  
    OP
       2015-08-25 20:53:33 +08:00
    @xhuuanniqege 我刚学没多久,请问您有没有什么好的学习建议,或者能不能推荐一些书以及博客之类的,不胜感激
    gengrui
        16
    gengrui  
       2015-08-25 21:56:38 +08:00
    看了下代码,说下个人看法。

    从 layout 上看,应该是一个 RelativeLayout ,在 adapter 里面应该是左边有一个 TextView, 右边有一个 ImageView ,左右两边分别有 20sp 的 padding 。我的看法其实和 @ssynhtn 一样,看起来是 TextView 覆盖了 ImageView ,因为 TextView 的 android:layout_width 属性是 match_parent ,建议你:
    1 改成"wrap_content",或者;
    2 在 TextView 里面加上 android:layout_toLeftOf="@+id/ImageView", 或者;
    3 把 TextView 和 ImageView 在 XML 文件里面换个位置,先定义 TextView ,再定义 ImageView 。

    我没有在代码里尝试,但是凭经验来说,上述任何一个方法应该都能起码把 ImageView 给显示出来。

    下面说说在 setOnClickListener 里面的 code 。一般来说不建议直接调用 getView 这个方法,因为这里面的逻辑是得到一个重绘的 View 。如果放在 for 循环从 0 到 list.getCount 的话,则相当于重绘这个 ListView 里面所有的 Item ,这是相当耗费资源的。建议你按照 @xhuuanniqege 提供的办法,在 Object 里面增加一个辅助的 flag 来定义这个 ImageView 的 visibility 。之后的 code 则类似于:

    // 假设你的 model 是 TextObject.class
    List<TextObject> mTextObjects = new ArrayList<TextObject> ();
    ListView mListView = (ListView ) findViewById (...);

    CustomAdapter<TextObject> mAdapter = new CustomAdapter (this, R.layout.xxx, mTextObjects );

    set.setOnClickListener (new OnClickListener () {
    @Override
    public void onClick (View v ) {
    //全选遍历 ListView 的选项,每个选项就相当于布局配置文件中的 RelativeLayout
    for (int i = 0; i < cityListView.getCount (); i++){
    mTextObjects.get (i ).setImageViewVisibility (true );
    }

    mAdapter.notifyDatasetChanged ();
    }
    });

    希望对你有帮助。
    ssynhtn
        17
    ssynhtn  
       2015-08-25 22:01:40 +08:00
    好吧,反正不是 textview 遮挡的问题,因为 textview 的背景色默认是透明的
    gengrui
        18
    gengrui  
       2015-08-25 22:07:13 +08:00
    @ssynhtn 有道理,这点确实忽略了
    creatorYC
        19
    creatorYC  
    OP
       2015-08-25 22:12:44 +08:00
    @gengrui 我刚接触 android 不久,我应该去详细了解一下 ListView 的机制和原理,您的意思是说我应该设置标记去更新相应的 item 的可见性,而不应该每次都是去重绘 item ,的确我没有考虑到这些,没有考虑到代码的优化,多谢您的指点,我会尝试着改变这个写法,谢谢您详细的解答
    veiz
        20
    veiz  
       2015-08-25 22:16:00 +08:00
    @creatorYC StackOverflow 或者 SegmentFault
    creatorYC
        21
    creatorYC  
    OP
       2015-08-25 22:16:17 +08:00
    @ssynhtn 那我就更困惑了,暂且不说代码优化,用这种拙劣的不优化的代码能不能实现我想要的效果呢?
    xhuuanniqege
        22
    xhuuanniqege  
       2015-08-25 22:19:41 +08:00 via Android   ❤️ 1
    @creatorYC 新手用这个方法也说明挺动脑筋的了,多写多练,多谷歌,能力很快会提高的
    gengrui
        23
    gengrui  
       2015-08-25 22:26:10 +08:00
    @creatorYC 不客气 :)
    ssynhtn
        24
    ssynhtn  
       2015-08-25 22:27:28 +08:00
    我知道问题了,原因是你主动调用了 getView ,下面是 SimpleAdapter 的 getView 方法,因为传入的 convertView 是 null ,所以会新建一个 view ,这个 view 不会被 ListView 使用,因为只有 ListView 调用 getView 返回的那个 view 会被 ListView 使用,所以你对这个 view 进行任何操作都是没有任何用的

    /**
    * @see android.widget.Adapter#getView (int, View, ViewGroup )
    */
    public View getView (int position, View convertView, ViewGroup parent ) {
    return createViewFromResource (position, convertView, parent, mResource );
    }

    private View createViewFromResource (int position, View convertView,
    ViewGroup parent, int resource ) {
    View v;
    if (convertView == null ) {
    v = mInflater.inflate (resource, parent, false );
    } else {
    v = convertView;
    }

    bindView (position, v );

    return v;
    }
    ssynhtn
        25
    ssynhtn  
       2015-08-25 22:28:15 +08:00
    我之前说 textview 覆盖 imageview 的那个解释是错误的,以为 textview 的 background 默认是透明的
    creatorYC
        26
    creatorYC  
    OP
       2015-08-25 22:36:50 +08:00
    @ssynhtn 好像挺深奥的。。。恕我愚昧,我想知道我应该怎么写呢。。
    creatorYC
        27
    creatorYC  
    OP
       2015-08-25 22:48:50 +08:00
    @Bown 按您的方法,我实现了想要的效果,您能帮忙讲一下这两个的区别吗,就是因为像 @ssynhtn 说的那样,主动调用 getView 方法导致重新创建了一个 view ?
    ssynhtn
        28
    ssynhtn  
       2015-08-25 22:51:34 +08:00
    @creatorYC 继承 BaseAdapter 或者 ArrayAdapter ,建一个 model 类{text, image, visible}作为一行, Adapter 中保持一个 List<Model>,当需要对某一行的图片进行隐藏的时候对那一行的 model 的 visible 设为 false ,接着调用 Adapter.notifyDatasetChanged
    如果听不懂的话,善用 Google ,而且我真的很推荐 udacity 的免费 Android 课程
    creatorYC
        29
    creatorYC  
    OP
       2015-08-25 22:55:20 +08:00
    @ssynhtn 谢谢您,我会认真看的,多谢
    kaient
        30
    kaient  
       2015-08-26 11:53:48 +08:00
    我觉得吧,如果你把 imageView 的的 GONE 变成 INVISIBLE 应该就对了
    creatorYC
        31
    creatorYC  
    OP
       2015-08-26 13:31:40 +08:00
    @xhuuanniqege 请问你们都是怎么谷歌的,翻墙了还是用什么 vpn 啊
    xhuuanniqege
        32
    xhuuanniqege  
       2015-08-26 18:41:35 +08:00 via Android
    @creatorYC 翻墙了
    wzg1015
        33
    wzg1015  
       2015-08-27 11:41:32 +08:00
    1.看见你的宽度啥都用 sp 就蛋疼
    2.贴代码就贴全, ImageView 的父布局都不贴,你怎么知道一定是这个 imageView 的问题? 或许就是你 item 的布局就错误了呢,导致 imageView 无处显示呢
    3.上面是布局,下面是代码,一个字乱,何况代码就光一个监听,你就知道其他地方都正确?或许我们累死累活的找 bug ,结果发现就因为你这句语句没调用呢
    4.ImageView image = (ImageView ) layout.getChildAt (0 ); 很少见到这种写法,哪天你把图片放后面了,然后就报错了
    5.RelativeLayout layout =(RelativeLayout ) cityListView.getAdapter ().getView (i, null, null ); 如同 @ssynhtn 所说,你这根本就是创建了一个新的 view ,你显示它的图片,并没有卵用。

    6.如果让我写这个,我会将需要显示的数据放在一个 List 集合中,其元素就是每一行需要显示的图片( id ),文字,其中有个属性为是否显示图片,默认为不显示。 将 List 集合传给 adapter 。当点击按钮的时候,修改 list 内部数据,调用 adapter 的 notifyOnDataChange 。 这样逻辑在 activiy 里实现,显示在 adpter 实现。
    creatorYC
        34
    creatorYC  
    OP
       2015-08-27 12:34:37 +08:00
    @wzg1015 我按 @Bown 说的那样,已经解决了,正如您说的那样,我用这个 RelativeLayout layout =(RelativeLayout ) cityListView.getAdapter ().getView (i, null, null ); 其实就是创建了一个新的 view ,我贴这些代码是因为我敢肯定是这里面的问题,因为我的功能都是一个个加的,都是一个个测试的,关于代码的质量,我的确需要提高很多,因为我才刚学不久,所以现在要求自己先把功能做出来,代码优化的确做的很差。不管怎样,都谢谢您的耐心解答,我会提高自己问问题的水平,抱歉
    quiz
        35
    quiz  
       2015-08-28 12:43:47 +08:00
    Golang 效率高了,但是应该算是一种退步吧:)
    https://www.dartlang.org/mobile/
    看看 google 想怎么玩儿
    quiz
        36
    quiz  
       2015-08-28 12:44:36 +08:00
    cha ,回错贴了~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4401 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 01:04 · PVG 09:04 · LAX 18:04 · JFK 21:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.