2 u012123938 u012123938 于 2016.05.08 23:41 提问

HorizontalScrollView在ListView被回收利用

我有一个ListView,子view是HorizontalScrollView,当我对的ListView中的第一个子HorizontalScrollView从右边滑动到左边,把删除按钮显示出来之后,我再操作ListView上下滚动时,ListView会复用之前滑动HorizontalScrollView的view.

图片说明

图片说明

我的代码:
ListView 子view布局:

 <?xml version="1.0" encoding="utf-8"?>
<com.example.administrator.myapplication.DeleteView
    android:id="@+id/GrafDelete_root"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#f1f1f1"
    android:scrollbars="none"
    >

    <LinearLayout
        android:id="@+id/ll_content"
        android:layout_width="920px"
        android:layout_height="180px"
        android:orientation="horizontal">

        <RelativeLayout
            android:id="@+id/re_select"
            android:layout_width="80px"
            android:layout_height="180px"
            android:background="@color/colorAccent">

            <ImageView
                android:id="@+id/select"
                android:layout_width="36px"
                android:layout_height="36px"
                android:layout_centerInParent="true"
                android:scaleType="centerCrop"/>
        </RelativeLayout>

        <AbsoluteLayout
            android:id="@+id/ab_view_bg"
            android:layout_width="720px"
            android:layout_height="180px">

            <TextView
                android:id="@+id/text_title"
                android:layout_width="450px"
                android:layout_height="80px"
                android:layout_x="20px"
                android:layout_y="30px"
                android:gravity="center_vertical"
                android:maxLines="2"
                android:textColor="#515151"
                android:textSize="30px"
                />
        </AbsoluteLayout>

        <AbsoluteLayout
            android:id="@+id/ab_delete_bg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <View
                android:id="@+id/view_delete_bg"
                android:layout_width="120px"
                android:layout_height="180px"
                android:background="#ff0000"
                ></View>

            <TextView
                android:id="@+id/text_delete"
                android:layout_width="120px"
                android:layout_height="60px"
                android:layout_y="90px"
                android:gravity="center"
                android:text="删除"
                android:textColor="#ffffff"
                android:textSize="30px"
                />
        </AbsoluteLayout>
    </LinearLayout>
</com.example.administrator.myapplication.DeleteView>

自定义的HorizontalScrollView,可以从又滑动到左边,显示被影藏的删除按钮:

 public class DeleteView extends HorizontalScrollView {
    private static final int SWIPE_MIN_DISTANCE = 5;
    private static final int SWIPE_THRESHOLD_VELOCITY = 600;
    private GestureDetector gestureDetector;

    private Context context;
    private int start;//开始滑动的位置
    private int end;//结束滑动的位置
    private VelocityTracker velocityTracker;
    private int width;//屏幕宽度的额外宽度。
    private boolean isEnableScroll;//是否打开水平滑动  true可以水平滑动  false不能水平滑动

    public DeleteView(Context context) {
        super(context);
    }

    public DeleteView(Context context, AttributeSet attrs) {
        super(context, attrs);
        width = 120;//设定额外的宽度
        gestureDetector = new GestureDetector(new MyGestureDetector());
    }

    public DeleteView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (velocityTracker == null) {
            velocityTracker = VelocityTracker.obtain();
        }
        if (!isEnableScroll)
            return true;
        if (gestureDetector.onTouchEvent(event))
            return true;

        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            start = (int) event.getRawX();
        }
        if (event.getAction() == MotionEvent.ACTION_UP) {
            end = (int) event.getRawX();
            if (start > end) {
                if (getScrollX() < width / 2 || velocityTracker.getXVelocity() > SWIPE_THRESHOLD_VELOCITY) {
                    smoothScrollTo(0, 0);
                } else {
                    smoothScrollTo(width, 0);
                }
            }
            if (start < end) {
                if (getScrollX() > width / 2 || velocityTracker.getXVelocity() > SWIPE_THRESHOLD_VELOCITY) {
                    smoothScrollTo(width, 0);
                } else {
                    smoothScrollTo(0, 0);
                }
            }
            velocityTracker.clear();
            return true;
        }
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            velocityTracker.addMovement(event);
        }
        return super.onTouchEvent(event);
    }

    class MyGestureDetector extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            if (!isEnableScroll)
                return true;
            try {
                //right to left
                if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    smoothScrollTo(width, 0);
                    return true;
                }
            } catch (Exception e) {
                // nothing
            }
            return false;
        }
    }

    //是否开启水平滑动
    public void setEnAbleHorizontalScroll(boolean b) {
        if (b == isEnableScroll) {
            return;
        } else {
            isEnableScroll = b;
        }
        if (!isEnableScroll) {
            smoothScrollTo(0, 0);
        }
    }
}

Adapter:

 public class ListAdapter extends BaseAdapter {
    private Context context;
    private ArrayList<ListItem> items;
    private boolean isAble;

    public ListAdapter(Context context, ArrayList<ListItem> items) {
        this.context = context;
        this.items = items;
    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public ListItem getItem(int position) {
        return items.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }


    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        final ViewHolder holder;
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = LayoutInflater.from(context).inflate(R.layout.list_item, null);
            holder.mDelete_Item_root = (DeleteView) convertView.findViewById(R.id.GrafDelete_root);
            holder.ll_content = (LinearLayout) convertView.findViewById(R.id.ll_content);
            holder.re_select = (RelativeLayout) convertView.findViewById(R.id.re_select);
            holder.select = (ImageView) convertView.findViewById(R.id.select);
            holder.ab_view_bg = (AbsoluteLayout) convertView.findViewById(R.id.ab_view_bg);
            holder.text_title = (TextView) convertView.findViewById(R.id.text_title);
            AbsoluteLayout ab_delete_bg = (AbsoluteLayout) convertView.findViewById(R.id.ab_delete_bg);
            holder.view_delete_bg = convertView.findViewById(R.id.view_delete_bg);
            TextView text_delete = (TextView) convertView.findViewById(R.id.text_delete);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        if (isAble) {//Open edit
            DeleteView.LayoutParams layoutParams = new DeleteView.LayoutParams(920, 180);
            layoutParams.leftMargin = 0;
            holder.ll_content.setLayoutParams(layoutParams);
            ((DeleteView) convertView).setEnAbleHorizontalScroll(false);
        } else {//Editing is not open
            DeleteView.LayoutParams layoutParams = new DeleteView.LayoutParams(920, 180);
            layoutParams.leftMargin = -80;
            holder.ll_content.setLayoutParams(layoutParams);
            ((DeleteView) convertView).setEnAbleHorizontalScroll(true);
        }

        if (items.get(position).isSelected) {
            holder.select.setBackgroundResource(R.mipmap.item_selected1);
        } else {
            holder.select.setBackgroundResource(R.mipmap.item_select_un);
        }
        View.OnTouchListener listener = new View.OnTouchListener() {
            boolean select_out, delete_out;

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        switch (v.getId()) {
                            case R.id.re_select:
                                select_out = false;
                                break;
                            case R.id.view_delete_bg:
                                holder.view_delete_bg.setAlpha(0.5f);
                                delete_out = false;
                                break;
                        }
                        break;
                    case MotionEvent.ACTION_MOVE:
                        switch (v.getId()) {
                            case R.id.re_select:
                                int x = (int) event.getX();
                                int y = (int) event.getY();
                                if (x < 0 || y < 0 || x > holder.re_select.getWidth() || y > holder.re_select.getHeight()) {
                                    select_out = true;
                                }
                                break;
                            case R.id.view_delete_bg:
                                int x2 = (int) event.getX();
                                int y2 = (int) event.getY();
                                if (x2 < 0 || y2 < 0 || x2 > holder.view_delete_bg.getWidth() || y2 > holder.view_delete_bg.getHeight()) {
                                    holder.view_delete_bg.setAlpha(1.0f);
                                    delete_out = true;
                                }
                                break;
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        switch (v.getId()) {
                            case R.id.re_select:
                                if (select_out) {
                                    return true;
                                }
                                if (items.get(position).isSelected) {
                                    items.get(position).isSelected = false;
                                    holder.select.setBackgroundResource(R.mipmap.item_select_un);
                                } else {
                                    items.get(position).isSelected = true;
                                    holder.select.setBackgroundResource(R.mipmap.item_selected1);
                                }
                                break;
                            case R.id.view_delete_bg:
                                holder.view_delete_bg.setAlpha(1.0f);
                                if (delete_out)
                                    return true;
                                if (isAble)
                                    return true;

                                break;
                        }
                        break;
                    case MotionEvent.ACTION_CANCEL:
                        switch (v.getId()) {
                            case R.id.re_select:
                                break;
                            case R.id.view_delete_bg:
                                holder.view_delete_bg.setAlpha(1.0f);
                                break;
                        }
                        break;
                }
                return true;
            }
        };
        holder.re_select.setOnTouchListener(listener);
        holder.view_delete_bg.setOnTouchListener(listener);
        holder.text_title.setText(items.get(position).mString);
        return convertView;
    }

    private class ViewHolder {
        TextView text_title;
        RelativeLayout re_select;
        ImageView select;
        AbsoluteLayout ab_view_bg, ab_delete_bg;
        View view_delete_bg;
        //        TextView text_delete;
        LinearLayout ll_content;
        DeleteView mDelete_Item_root;
    }

    /**
     * You can edit
     */
    public void setIsAble(boolean b) {
        if (b == isAble) {
            return;
        } else {
            isAble = b;
            notifyDataSetChanged();
        }

    }
}

Activity:

 public class MainActivity extends AppCompatActivity {
    private ListView listView;
    private TextView tv_button;
    private ArrayList<ListItem> mArrayList = new ArrayList<>();
    private boolean isAble;
    private ListAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.listView);
        tv_button = (TextView) findViewById(R.id.tv_button);

        getListItem();
        adapter = new ListAdapter(this, mArrayList);
        listView.setAdapter(adapter);

        setListener();
    }

    public void setListener() {
        View.OnTouchListener listener = new View.OnTouchListener() {
            boolean delete_out;

            @Override
            public boolean onTouch(View arg0, MotionEvent arg1) {
                switch (arg1.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        switch (arg0.getId()) {
                            case R.id.tv_button:
                                tv_button.setAlpha(0.5f);
                                delete_out = false;
                                break;
                        }
                        break;
                    case MotionEvent.ACTION_MOVE:
                        switch (arg0.getId()) {
                            case R.id.tv_button:
                                int x2 = (int) arg1.getX();
                                int y2 = (int) arg1.getY();
                                if (x2 < 0 || y2 < 0 || x2 > tv_button.getWidth() || y2 > tv_button.getHeight()) {
                                    tv_button.setAlpha(1.0f);
                                    delete_out = true;
                                }
                                break;
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        switch (arg0.getId()) {
                            case R.id.tv_button:
                                tv_button.setAlpha(1.0f);
                                if (delete_out)
                                    return true;
                                changeState();
                                break;
                        }
                        break;
                    case MotionEvent.ACTION_CANCEL:
                        switch (arg0.getId()) {
                            case R.id.tv_button:
                                tv_button.setAlpha(1.0f);
                                break;
                        }
                }
                return true;
            }
        };
        tv_button.setOnTouchListener(listener);
    }
    //点编辑或完成时切换状态
    private void changeState() {
        if (isAble) {
            isAble = false;
            tv_button.setText("编辑");
        } else {
            isAble = true;
            tv_button.setText("完成");
        }
        adapter.setIsAble(isAble);
    }
    private void getListItem() {
        for (int i = 0; i < 20; i++) {
            ListItem listItem = new ListItem();
            listItem.mString = i + "\t" + "ScrollView and HorizontalScrollView conflict";

            mArrayList.add(listItem);
        }
    }
}

8个回答

l_vaule
l_vaule   2016.05.09 00:13

你用了viewholder他当然会复用啦。。

q610098308
q610098308   2016.05.09 09:14

这个状态应该保存到 isAble,默认全部重新置为 false;

u012123938
u012123938 您说的是什么意思?我把我的问题更改了一下,您在看看
一年多之前 回复
u012123938
u012123938 您说的是什么意思?我把我的问题更改了一下,您在看看
一年多之前 回复
u012123938
u012123938 您说的是什么意思?我把我的问题更改了一下,您在看看
一年多之前 回复
u012123938
u012123938 您说的是什么意思?我把我的问题更改了一下,您在看看
一年多之前 回复
l_vaule
l_vaule   2016.05.09 16:19

你用的ViewHolder的原理就是为了避免ListView中子条目重复去加载,也就是说你的所有listview的子条目都是用的一个ViewHolder对象中的控件,但是ListView只会先加载显示出来的条目,当显示出来之后你又把条目改变了,也就是说你把ViewHolder对象中的DeleteView改变了,当你滑动的时候又去getView,再次拿到的条目虽然也是ViewHolder,但是其中的DeleteView已经让你滑动了,他当然会显示你滑动的条目。

u012123938
u012123938 用720x1080的屏幕查看
一年多之前 回复
u012123938
u012123938 这是我的demo:http://download.csdn.net/detail/u012123938/9514712
一年多之前 回复
u012123938
u012123938 这是我的demo:http://download.csdn.net/detail/u012123938/9514712
一年多之前 回复
u012123938
u012123938 嗯嗯,,您说的非常有道理,说的思路也非常明确,但是我想知道,怎么让他不回收利用,怎么改变回收
一年多之前 回复
l_vaule
l_vaule   2016.05.09 20:44

简单的看了下你的代码,这里我给你提供一下我的思想法,首先你需要一个装int类型的position数组,这个数组用来记录哪个item是滑动的(也就是删除漏出来的),也就是说如果删除是漏出来的话,你就要把这个position装进去,如果又划回去(删除又看不见了),则从数组移除。在getView的时候,去遍历这个数组,如果删除应该露出来就让layoutParams.leftMargin = -80;否则的话=0.如果还不能解决的话再和我说。

l_vaule
l_vaule 回复u012123938: 我改好了
一年多之前 回复
u012123938
u012123938 大牛??我试了一下不得行,我是这么想的当我把删除画出来之后,我就以把position存到ArrayList中,但是我始终拿不到position
一年多之前 回复
l_vaule
l_vaule   2016.05.10 00:08

我不是什么大牛,我感觉position肯定能拿到的,明天早上我试一下吧。

u012123938
u012123938 我是在我的项目里面分离出来做测试的。。然后不想再算像素了,我们默认的是720x1080,所以用720x1080的手机测试
一年多之前 回复
l_vaule
l_vaule OK了,我不知道你为什么原来整的是px,让我改成dp了,px本身人家就不推荐用,我的手机都显示的变形严重,我的电脑用的有点久了,Studio太卡了,我给项目改成eclipse了,你自己来下载吧。
一年多之前 回复
u012123938
u012123938 我的那个demo相当于是可以多选也可以单选
一年多之前 回复
u012123938
u012123938 我的那个demo相当于是可以多选也可以单选
一年多之前 回复
u012123938
u012123938 啥问题???我的那个demo相当于是可以选也可以单选
一年多之前 回复
l_vaule
l_vaule 回复u012123938: 改好了是改好了,但又出来新问题了。。
一年多之前 回复
u012123938
u012123938 怎么样了???
一年多之前 回复
l_vaule
l_vaule 嗯(´-ω-`),晚安
一年多之前 回复
u012123938
u012123938 ok晚安,可以直接用我的demo
一年多之前 回复
l_vaule
l_vaule   2016.05.10 11:51

我去吃饭了下午给你传,已经改好了。

l_vaule
l_vaule 我发你邮箱了。
一年多之前 回复
u012123938
u012123938 我打开不了,,一直都是404
一年多之前 回复
l_vaule
l_vaule http://download.csdn.net/detail/l_vaule/9515306
一年多之前 回复
u012123938
u012123938 1129826190@qq.com我的邮箱
一年多之前 回复
l_vaule
l_vaule   2016.05.10 12:36

划回去我不记得remove了,你自己加一下吧。。刚看到

qq_34263321
qq_34263321   2016.07.04 20:52

convertView判断了是否为null,是null你就new,否则你就复用回收。我是新手

Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!