关于View的知识点

View控件是安卓中非常重要的一个概念,下边将总结关于View工作原理的一些重要知识点(更新中…)

View 的位置左边参数


Scroller的使用

当我们使用View中的scrollTo/scrollBy方法来进行滑动时,过程是瞬间完成的,没有滑动的效果,为了能达到更好的用户体验,我们需要借助Scroller这个对象来完成,下边结合代码实例介绍


    <!--MyView继承自TextView,内部添加一些文本方便看到滑动的效果-->
    <com.wj.testdemo.MyView
            android:id="@+id/my_view"
            android:layout_width="match_parent"
            android:layout_height="3000dp"
            android:text="a\nb\nb\nb\nb\nb\nb\nb\nb\nb\nb"
            android:background="#abcdef"/>

在MyView中添加如下代码,使用Scroller辅助View的内容平滑滑动


    // 全局的Scroller对象
    Scroller mScroller = new Scroller(getContext());

    /**
     * 平滑滑动的方法,使用这个方法滑动这个View的content
     * @param destX 结果位置的x坐标
     * @param destY 结果位置的y坐标
     */
    public void smoothScrollTo(int destX, int destY){
        // 通过当前位置计算得出结果位置坐标
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        int deltaX = destX - scrollX;
        int deltaY = destY - scrollY;

        // 这个方法主要给Scroller内部变量赋值
        mScroller.startScroll(scrollX, scrollY, deltaX, deltaY, 1000);
        invalidate();   // 使控件重绘
    }

    @Override
    public void computeScroll() {
        // 此处忽略了其父类TextView中的实现,重写
        // 如果scroller没有达到结果位置,返回true,继续调用View的scrollTo方法,加入消息队列
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

以下是View中这个computeScroll方法的定义,可以看到它是空实现,具体实现由开发者来做。


    /**
     * Called by a parent to request that a child update its values for mScrollX
     * and mScrollY if necessary. This will typically be done if the child is
     * animating a scroll using a {@link android.widget.Scroller Scroller}
     * object.
     */
    public void computeScroll() {
    }

以上注释的意思就是,子类在有必要更新mScrollX、mScrollY两个值时,会被父类调用,典型情况就是子类在使用Scroller做一个有动画效果的滚动时。上述写法是固定的。

使用Scroller的过程描述:

我们调用了startScroll方法,它的作用是给Scroller对象的参数赋值,来辅助记录动画的状态信息,然后具体事情是由invalidate方法来做的,他会导致View的重绘,然后在View的draw方法中又会调用那个我们自己实现的computeScroll方法,在这个方法中我们通过Scroller自身记录的状态参数得到当前View的坐标信息(通过调用computeScrollOffset获得,返回true说明,View动画还没有执行到我们预期的终点),然后接着我们调用View的scrollTo方法去实现View的滑动,并继续调用postInvalidate方法去调用invalidate方法重绘View,重复这个过程知道View滑动到我们给定的终点位置。

以下是上述提到方法的源码:

computeScrollOffset


    /**
     * Call this when you want to know the new location.  If it returns true,
     * the animation is not yet finished.
     * 返回true,动画未完成,false,动画已完成
     */ 
    public boolean computeScrollOffset() {
        // 判断动画是否已经结束
        if (mFinished) {
            return false;
        }

        // 当前时间 - 动画开始时间 = 经过的时间
        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);

        // 经过的时间 < 动画持续时间
        if (timePassed < mDuration) {
            switch (mMode) {
            case SCROLL_MODE:   // 滑动模式
                final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
                // 通过插值器算出滑动的当前比例 * 总滑动距离,然后四舍五入后加上起始值得当前的绝对坐标
                mCurrX = mStartX + Math.round(x * mDeltaX);
                mCurrY = mStartY + Math.round(x * mDeltaY);
                break;
            case FLING_MODE:    // 惯性滑动模式
                final float t = (float) timePassed / mDuration;
                final int index = (int) (NB_SAMPLES * t);
                float distanceCoef = 1.f;
                float velocityCoef = 0.f;
                if (index < NB_SAMPLES) {
                    final float t_inf = (float) index / NB_SAMPLES;
                    final float t_sup = (float) (index + 1) / NB_SAMPLES;
                    final float d_inf = SPLINE_POSITION[index];
                    final float d_sup = SPLINE_POSITION[index + 1];
                    velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
                    distanceCoef = d_inf + (t - t_inf) * velocityCoef;
                }

                mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;

                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
                // Pin to mMinX <= mCurrX <= mMaxX
                mCurrX = Math.min(mCurrX, mMaxX);
                mCurrX = Math.max(mCurrX, mMinX);

                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
                // Pin to mMinY <= mCurrY <= mMaxY
                mCurrY = Math.min(mCurrY, mMaxY);
                mCurrY = Math.max(mCurrY, mMinY);

                if (mCurrX == mFinalX && mCurrY == mFinalY) {
                    mFinished = true;
                }

                break;
            }
        }
        else {
            // 已到达结束时间,直接将终点值设置为当前的坐标
            mCurrX = mFinalX;
            mCurrY = mFinalY;
            mFinished = true;
        }
        return true;
    }

改变LayoutParams参数实现View的滑动


    ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) click2Test.getLayoutParams();
    if(params.width == ViewGroup.LayoutParams.WRAP_CONTENT){
        params.width = 50;
    }else {
        params.width += 100;
    }
    params.leftMargin += 100;

    click2Test.requestLayout();
    // 下边这行代码同效果,注意如果view是包裹内容(wrap_content),当改变其width时,起初的width会是-2(wrap_content的值)
    // click2Test.setLayoutParams(params);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值