AndroidBanner - ViewPager 03

科技资讯 投稿 10000 0 评论

AndroidBanner - ViewPager 03

AndroidBanner - ViewPager 03

    当banner不可见的时候,也需要停止轮播
  1. 给banner设置点击事件,长时间的触摸也会被默认是一个点击事件

这篇文章就来解决这些问题,并处理一下banner的曝光打点问题。

解决banner 不可见依旧轮播的问题

当Banner添加到屏幕上,且对用户可见的时候,可以开始轮播
当Banner从屏幕上移除,或者Banner不可见的时候,可以停止轮播
当手指触摸到Banner时,停止轮播
当手指移开时,开始轮播

OnAttachStateChangeListenner

该接口可以通知我们view添加到屏幕上或者从屏幕上被移除,或者可以直接重写view的onAttachedToWindow和onDetachedFromWindow方法

// view提供的接口,可以通过 addOnAttachStateChangeListener 添加舰艇
public interface OnAttachStateChangeListener {  
	public void onViewAttachedToWindow(@NonNull View v;  
	public void onViewDetachedFromWindow(@NonNull View v;  
}

// 复写view的方法
override fun onAttachedToWindow( {  
    super.onAttachedToWindow(  
}  
  
override fun onDetachedFromWindow( {  
    super.onDetachedFromWindow(  
}

这里我们通过复写方法的方式处理

onVisibilityChanged

protected void onVisibilityChanged(@NonNull View changedView, @Visibility int visibility {  
}

onWindowVisibilityChanged

view 提供了方法,可以复习该方法,当前widow的可见性发生变化的时候,会调用通知给我们

protected void onWindowVisibilityChanged(@Visibility int visibility {  
    if (visibility == VISIBLE {  
        initialAwakenScrollBars(;  
    }  
}

我们根据上面的api,可以封装一个接口,来监听View的可见性

VisibleChangeListener

interface VisibleChangeListener {  
    /**  
     * view 可见  
     */  
    fun onShown(  
  
    /**  
     * view 不可见  
     */  
    fun onDismiss(  
}

Banner重写方法,进行调用

override fun onVisibilityChanged(changedView: View, visibility: Int {  
    Log.e(TAG, "onVisibilityChanged ${changedView == this}, vis: $visibility"  
    dispatchVisible(visibility  
}  
  
override fun onWindowVisibilityChanged(visibility: Int {  
    super.onWindowVisibilityChanged(visibility  
    Log.e(TAG, "onWindowVisibilityChanged $visibility"  
    dispatchVisible(visibility  
}  
  
override fun onAttachedToWindow( {  
    super.onAttachedToWindow(  
    Log.e(TAG, "onAttachedToWindow "  
    this.mAttached = true  
}  
  
override fun onDetachedFromWindow( {  
    Log.e(TAG, "onDetachedFromWindow "  
    super.onDetachedFromWindow(  
    this.mAttached = false  
}

private fun dispatchVisible(visibility: Int {  
    val visible = mAttached && visibility == VISIBLE  
    if (visible {  
        prepareLoop(  
    } else {  
        stopLoop(  
    }  
    mVisibleChangeListener?.let {  
        when (visible {  
            true -> it.onShown(  
            else -> it.onDismiss(  
        }  
    }  
}

页面滚动时处理banner轮播

滚动监听,如果是scrollview,就监听滚动事件处理即可。如果是listview,recyclerview可以选择监听onscrollstatechanged,更高效。
下面是scrollview的监听处理

mBinding.scrollView.setOnScrollChangeListener(object :OnScrollChangeListener{  
    override fun onScrollChange(  
        v: View?,  
        scrollX: Int,  
        scrollY: Int,  
        oldScrollX: Int,  
        oldScrollY: Int  
     {  
        val visible = mBinding.vpBanner.getGlobalVisibleRect(Rect(  
        Log.e(TAG,"banner visible : $visible"  
        if(visible{  
            mBinding.vpBanner.startLoop(  
        }else{  
            mBinding.vpBanner.stopLoop(  
        }  
    }  
}

点击事件的处理

首先要声明一个点击事件回调接口

interface PageClickListener {  
    fun onPageClicked(position: Int  
}

重写banner的onTouch事件,将移动距离小于100,且按压时间小于500ms的事件认为是点击事件

private var mMoved = false  
private var mDownX = 0F  
private var mDownY = 0F  
  
/**  
 * 当前事件流结束时,恢复touch处理的相关变量  
 */  
private fun initTouch( {  
    this.mMoved = false  
    this.mDownX = 0F  
    this.mDownY = 0F  
}  
  
private fun calculateMoved(x: Float, y: Float, ev: MotionEvent {  
    mClickListener?.let {  
        // 超过500ms(系统默认的时间 我们认为不是点击事件  
        if (ev.eventTime - ev.downTime >= 500 {  
            return  
        }  
        // 移动小于阈值我们认为是点击  
        if (sqrt(((x - mDownX.pow(2 + (y - mDownY.pow(2 >= MOVE_FLAG {  
            return  
        }  
        val count = adapter?.count ?: 0  
        if (count == 0 {  
            return  
        }  
        // 由于我们实现无限轮播的方式是重新设置当前选中的item,这里要将currentItem重新映射回去  
        val index = when (currentItem {  
            in 1..count - 2 -> currentItem - 1  
            0 -> count - 1  
            else -> 0  
        }  
        it.onPageClicked(index  
    }  
}  
  
override fun onTouchEvent(ev: MotionEvent?: Boolean {  
    when (ev?.action {  
        MotionEvent.ACTION_DOWN -> {  
            this.mDownY = ev.y  
            this.mDownX = ev.x  
            stopLoop(  
        }  
        MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {  
            val y = ev.y  
            val x = ev.x  
            calculateMoved(x, y, ev  
            initTouch(  
            prepareLoop(  
        }  
    }  
    return super.onTouchEvent(ev  
}

曝光打点的处理

监听page切换,当page变化的时候,从实际展示的数据队列中取出数据进行曝光。

class ExposureHelper(private val list: List<*>, private var last: Int = -1 :  
    ViewPager.OnPageChangeListener {  
  
    private var mStart: AtomicBoolean = AtomicBoolean(false;  
  
    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int =  
        Unit  
  
    override fun onPageSelected(position: Int {  
        Log.e(TAG, "$position $last"  
        if (last >= 0 {  
            exposure(  
        }  
        last = position  
    }  
  
    override fun onPageScrollStateChanged(state: Int = Unit  
  
    /**  
     * 开始曝光  
     * @param current Int  
     */    fun startExposure(current: Int {  
        mStart.set(true  
        last = current  
    }  
  
    /**  
     * 停止曝光  
     */  
    fun endExposure( {  
        if (mStart.get( {  
            mStart.set(false  
            exposure(  
        }  
    }  
  
    /**  
     * 实际执行数据上报的处理  
     */  
    private fun exposure( {  
        val data = list[last]  
        Log.e(TAG, "data:$data"  
    }  
  
    companion object {  
        private const val TAG = "ExposureHelper"  
    }  
}

VPAdapter 对外提供实际展示的数据集

private val mData = mutableListOf<T>(  
  
fun setData(data: List<T> {  
    mData.clear(  
    if (this.loop && data.size > 1 {  
        // 数组组织一下,用来实现无限轮播  
        mData.add(data[data.size - 1]  
        mData.addAll(data  
        mData.add(data[0]  
    } else {  
        mData.addAll(data  
    }  
}

fun getShowDataList(:List<T>{  
    return mData  
}

在Banner中的配置使用

private var mExposureHelper: ExposureHelper? = null

/**  
 * 自动轮播  
 */  
fun startLoop( {  
    if (mLoopHandler == null {  
        mLoopHandler = Handler(Looper.getMainLooper( { message ->  
            return@Handler when (message.what {  
                LOOP_NEXT -> {  
                    loopNext(  
                    true  
                }  
                else -> false  
            }  
        }  
    }  
    if (mLoopHandler?.hasMessages(LOOP_NEXT != true {  
        Log.e(TAG, "startLoop"  
        mLoopHandler?.sendEmptyMessageDelayed(LOOP_NEXT, mLoopDuration  
    }  
    // 开始轮播时开始曝光(可见时会触发轮播)  
    mExposureHelper?.startExposure(currentItem  
}  
  
fun stopLoop( {  
    // 停止轮播时结束曝光(不可见时会停止轮播)  
    mExposureHelper?.endExposure(  
    mLoopHandler?.removeMessages(LOOP_NEXT  
}

fun bindExposureHelper(exposureHelper: ExposureHelper? {  
    mExposureHelper = exposureHelper  
    mExposureHelper?.let {  
        addOnPageChangeListener(it  
    }  
    mExposureHelper?.startExposure(currentItem  
}

代码:huyuqiwolf/Banner (github.com

编程笔记 » AndroidBanner - ViewPager 03

赞同 (51) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽