photo player 显示 ☞ 列表选中项的处理

photo player 显示☞列表选中项的处理

一、需求

  1. 需要显示一个图片列表,可以左右滑动,点击则在大图显示该图;
  2. 可以在界面上显示大图,可以左右滑动切换图片,可以支持zoom、move、rotate;
  3. 大图与图片列表相互关联:
    • 点击列表item可以在show对应大图;
    • 大图切换则列表保持该Item在列表中间位置;
    • 对于大图显示的图片添加红框标记(item的两个背景);

效果如此图:
在这里插入图片描述

二、当前方案

  1. 大图显示使用ViewPager控件实现,则可以支持左右滑动切换图片功能(支持预加载);
  2. 图片列表使用RecyclerView控件来实现,则可以支持列表功能,同时其缓存机制对于机器更加友好;
  3. 列表关联功能可以自行实现:
    • 点击item show对应大图:对每个item添加点击事件监听,点击则设置viewpager显示指定position图片;
    • 添加对于ViewPager的滑动监听,添加对于列表移动的处理;
    • 显示的图片在列表中设置红框标记,这里前后使用两个方案实现:
      1. 使用arraylist维护对应的标记,在show图片时添加判断,viewpager切换则刷新列表显示;
      2. 在adapter中维护一个变量,记录当前显示的图片,viewpager切换则刷新列表中背景切换的两个item,且仅处理其背景,并不刷新该item中imageview;

本次记录的重点在于红框标记的两个方案实现,方案2比方案1节省了大量资源:列表、刷新item数量、控件

三、关联具体实现

3.1 方案一:

  1. adapter中维护一个boolean类型的list,大小与imagelist相同,选中设置为true,其余设置为false:

    public List<Boolean> isClicks;
    //控件是否被点击,默认为false,如果被点击,改变值,控件根据值改变自身颜色
    isClicks = new ArrayList<>();
    for (int i = 0; i < mImageList.size(); i++) {
        isClicks.add(false);
    }
    
  2. 在init list item view的时候,判断上述值:

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
    	...
        if (isClicks.get(position)) {
            imageItem.setBackgroundResource(R.drawable.photo_preview__btn_p);
        } else {
            imageItem.setBackgroundResource(R.drawable.photo_preview__btn_n);
        }
    	...
    }
    
  3. ViewPager滑动的时候改变对应的值,并更新:

    private NoPreloadViewPager.OnPageChangeListener mPhotoPageChangeListener = new NoPreloadViewPager.OnPageChangeListener() {
    
        @Override
        public void onPageSelected(int position) {
            mPosition = position;
            mRecycleAdapter.isClicks.set(mPosition, true);
            for(int i = 0; i <mRecycleAdapter.isClicks.size();i++){
            	mRecycleAdapter.isClicks.set(i,false);
        	}
       		mRecycleAdapter.isClicks.set(position,true);
        	//此接口会将所有可见的item重新刷新一遍
            mRecycleAdapter.notifyDataSetChanged();
        	MoveToPosition(mLayoutManager, mPhotoListView, position);
        }
    
        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }
    
        @Override
        public void onPageScrollStateChanged(int arg0) {
        //arg0 ==1的时表示正在滑动,arg0==2的时表示滑动完毕了,arg0==0的时表示什么都没做。
            if (arg0 == 0) {
            } else if (arg0 == 1) {
            } else if (arg0 == 2) {
            }
        }
    };
    
    //根据当前view切换将要显示的item移动到中间位置
    public void MoveToPosition(LinearLayoutManager manager, RecyclerView recyclerView, int position) {
        int firstItem = manager.findFirstVisibleItemPosition();
        int lastItem = manager.findLastVisibleItemPosition();
        int offset = (lastItem - firstItem) / 2;
        int maxItem = mPhotoList.size() - 1;
        int center = (lastItem + firstItem) / 2;
        int offsetPosition = 0;
    
        if (position < center) {
            offsetPosition = position - offset;
        } else if (position == center) {
            offsetPosition = position;
        } else {
            offsetPosition = position + offset;
        }
    
        if (offsetPosition < 0) {
            offsetPosition = 0;
        } else if (offsetPosition > maxItem) {
            offsetPosition = maxItem;
        }
        recyclerView.scrollToPosition(offsetPosition);
    }
    
  4. 列表item点击时:

    private PhotoListAdapter.Callback mPhotoListItemCallback = new PhotoListAdapter.Callback() {
        @Override
        public void onItemClick(Image image, ImageView imageItem) {
            mPosition = mPhotoList.indexOf(image);
            //todo set the current item in center
            mPhotoView.setCurrentItem(mPosition);
        }
    };
    

    这里在点击列表item的时候并没有做notify item列表的处理:1. 这里两个控件要避免操作成环;
    2. ViewPager控件中的setCurrentItem接口,在其内部实现中会调用到:mOnPageChangeListener.onPageSelected(item);

    /**
         * Set the currently selected page. If the ViewPager has already been through its first
         * layout there will be a smooth animated transition between the current item and the
         * specified item.
         * @param item Item index to select
         */
    public void setCurrentItem(int item) {
        mPopulatePending = false;
        setCurrentItemInternal(item, !mFirstLayout, false);
    }
    
    /**
         * Set the currently selected page.
         * @param item Item index to select
         * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately
         */
    public void setCurrentItem(int item, boolean smoothScroll) {
        mPopulatePending = false;
        setCurrentItemInternal(item, smoothScroll, false);
    }
    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
        setCurrentItemInternal(item, smoothScroll, always, 0);
    }
    
    void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
        if (mAdapter == null || mAdapter.getCount() <= 0) {
            setScrollingCacheEnabled(false);
            return;
        }
        if (!always && mCurItem == item && mItems.size() != 0) {
            setScrollingCacheEnabled(false);
            return;
        }
        if (item < 0) {
            item = 0;
        } else if (item >= mAdapter.getCount()) {
            item = mAdapter.getCount() - 1;
        }
        final int pageLimit = mOffscreenPageLimit;
        if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
            // We are doing a jump by more than one page.  To avoid
            // glitches, we want to keep all current pages in the view
            // until the scroll ends.
            for (int i=0; i<mItems.size(); i++) {
                mItems.get(i).scrolling = true;
            }
        }
    
        final boolean dispatchSelected = mCurItem != item;
        mCurItem = item;
        populate();
        final int destX = (getWidth() + mPageMargin) * item;
        if (smoothScroll) {
            smoothScrollTo(destX, 0, velocity);
            if (dispatchSelected && mOnPageChangeListener != null) {
                mOnPageChangeListener.onPageSelected(item);
            }
        } else {
            if (dispatchSelected && mOnPageChangeListener != null) {
                mOnPageChangeListener.onPageSelected(item);
            }
            completeScroll();
            scrollTo(destX, 0);
        }
    }
    

    所以这里按照上述方式实现:

    1. 维护一个list;
    2. 每次更新都在重新设置一遍list;
    3. notify 数据变化时,当前可见的item都要刷新一遍;

3.2 方案二:

基于上述方案进行优化:

  1. 由于每次选中的图片仅一个,则可以使用一个变量来维护;
  2. 每次在view切换的时候,现在是更新整个list,而这里可以仅设置一个值;
  3. 每次在view切换之后需要通知列表更新数据,现在是更新所有可见列表,这里可以仅更新两个item;
  4. 每次更新view的时候,都是将这个view完整的重新加载,这里可以仅更新item的背景图,而不去重新加载图片;

则具体来看code实现:

  1. 使用变量来标记当前显示的图片:

    //M: add select solution
    private int selectPosition = -1;
    public void setSelectPosition(int position) {
        if (0 > position ||mImageList.size() < position ){
            Debug.i(TAG, "position error: " + position);
            return;
        }
    	selectPosition = position;
    }
    public int getSelectPosition( ) {
    	return selectPosition;
    }
    
  2. 获取此前一次显示的位置,更新当前显示的位置,并通知adapter有数据变化::

    //add viewpager item click listener
    private NoPreloadViewPager.OnPageChangeListener mPhotoPageChangeListener = new NoPreloadViewPager.OnPageChangeListener() {
    
        int oldPosition = 0;
        @Override
        public void onPageSelected(int position) {
            mPosition = position;
            //获取前一个显示的position,并设置当前显示的position;
            oldPosition = mRecycleAdapter.getSelectPosition();
            mRecycleAdapter.setSelectPosition(position);
            //注意,这里更新使用notifyItemChanged,仅更新上述两个位置的item
            //第二个参数为0表示payloads不为空,是只更新item某一个控件,而不是完整更新的关键
            mRecycleAdapter.notifyItemChanged(oldPosition, 0);
            mRecycleAdapter.notifyItemChanged(position, 0);
    		//照例让RecyclerView滑动到指定位置
            MoveToPosition(mLayoutManager, mPhotoListView, position);
        }
    
        @Override
        public void onPageScrolled(int position, float arg1, int arg2) {
        }
    
        @Override
        public void onPageScrollStateChanged(int arg0) {
            //arg0 ==1的时表示正在滑动,arg0==2的时表示滑动完毕了,arg0==0的时表示什么都没做。
            if (arg0 == 0) {
            } else if (arg0 == 1) {
            } else if (arg0 == 2) {
            }
        }
    };
    
  3. adapter中处理仅更新item的背景图:

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
    }//此函数不去实现,转而实现下述带payloads参数的接口
    
    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position, List payloads) {
        final Image image = mImageList.get(position);
        final ImageView imageItem = holder.imageItem;
        final ProgressBar loading = holder.loading;
        final String imagePath = image.getImagePath();
        if(payloads.isEmpty()) {//这里是关键:正常刷新时走这里,主要是load图片和添加click监听
            imageItem.setImageDrawable(mContext.getDrawable(R.drawable.photo_preview__btn_n));
            loading.setVisibility(View.VISIBLE);
            holder.imageItem.setTag(imagePath);
            mImageLoader.loadImage(holder.imageItem,
                                   imagePath,
                                   true,
                                   position,
                                   new ImageLoader.onImageLoadedListener() {
                                       @Override
                                       public void displayImage(ImageView view, Bitmap bitmap) {
                                           //check if there is the correct view
                                           String path = view.getTag().toString();
                                           if (path.equals(imagePath)) {
                                               view.setImageBitmap(bitmap);
                                               loading.setVisibility(View.GONE);
                                           }
                                       }
                                   });
            if (mCallback != null) {
                holder.imageItem.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mCallback.onItemClick(position, imageItem);
                    }
                });
            }
        } else {//选中图片背景图更新时走这里,只判断position更新背景图
            //set background resource
            //init or select all need set the background rescource, so move it 
            // to this function out
        }
        if (position == selectPosition) {
            imageItem.setBackgroundResource(R.drawable.photo_preview__btn_p);
        } else {
            imageItem.setBackgroundResource(R.drawable.photo_preview__btn_n);
        }
    }
    

    整体来讲改动比较小,但是加载图片时对于资源的消耗要降低了很多

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值