Android

Android 实现TwitterCoverScrollView

2017-09-18  本文已影响36人  Flynn_X

先看实现的效果

TIM图片20170918154711.gif

为什么要去实现这个效果

在我写这篇文章前你可能看了相关实现该效果的方法,天下文章一大抄,各种实现的源码,讲解,你会发现其实都是出自某一个原创作者,当然,我不喜欢全盘照抄,没有自己的体会或者实现过程,一般也不写相关文章,我相信有人看过 TwitterCover-Android,这个效果是继承listview的基础上实现的,但是当我要写【设置】页面或者【我的】页面时,其实是不大适合使用listview去实现的。

 TwitterCoverListView listView = (TwitterCoverListView) fview.findViewById(R.id.layout_listview);
        listView.setHeaderImage(BitmapFactory.decodeResource(getResources(), R.mipmap.per_bg));
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_expandable_list_item_1,
                new String[]{}
        );

        View headView1 = listView.getHeaderView();
        CircleImageView avatar = (CircleImageView) headView1.findViewById(R.id.avatar);
        TextView nickname = (TextView) headView1.findViewById(R.id.nickname);
        TextView authentication = (TextView) headView1.findViewById(R.id.authentication);
        TextView publicInfo = (TextView) headView1.findViewById(R.id.publicInfo);

        View headView2 = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_tab3_headview,null);
        listView.addHeaderView(headView2);
        listView.setAdapter(adapter);

这也就导致我放弃使用这个开源控件的原因,并且想办法去实现基于srcollview的效果。

TwitterCoverScrollView实现了那些功能。

TwitterCover-Android 源码分析。

在写TwitterCoverScrollView前,我对TwitterCover-Android 进行了源码分析,大致如下。

image.png

1、继承listview。
2、init读取配置数据 并通过addheadview添加顶部视图。
3、overScrollby 方法监听下拉,通过requestLayout 实现视图刷新达到视图放大缩小效果。
4、stackBlur 实现模糊效果。
5、onTouchEvent 监听MotionEvent.ACTION_UP 动作实现释放回弹效果。

TwitterCoverScrollView 使用方法。

1、将原来scrollview替换成相应的TwitterCoverScrollView
2、在TwitterCoverScrollView 下按照正常习惯写布局,但要求钱三层必须是如下结构

image.png
    <com.example.xsl.twittercoverscrollview_android.TwitterCoverScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">


            <FrameLayout
                android:layout_width="match_parent"
                android:layout_height="200dp">

                <ImageView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:adjustViewBounds="true"
                    android:scaleType="centerCrop"
                    android:src="@mipmap/fuwu_banner" />

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:orientation="vertical">

                    <ImageView
                        android:layout_width="80dp"
                        android:layout_height="80dp"
                        android:adjustViewBounds="true"
                        android:scaleType="centerCrop"
                        android:src="@mipmap/ic_launcher_round" />

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="名字"
                        android:textColor="#666666" />

                </LinearLayout>
            </FrameLayout>


        </LinearLayout>

    </com.example.xsl.twittercoverscrollview_android.TwitterCoverScrollView>

3、其他无任何要求,在FrameLayout内写顶部效果布局。其中imageview设置 android:scaleType="centerCrop"实现动态裁剪视图。
4、刷新监听方法,一次下拉只会刷新一次。

  TwitterCoverScrollView twitterCoverScrollView = (TwitterCoverScrollView) findViewById(R.id.scrollView);
        twitterCoverScrollView.setOnRefreshListener(new TwitterCoverScrollView.OnRefreshListener() {
            @Override
            public void onRefresh() {
                Log.e("setOnRefreshListener","刷新了");
            }
        });

以上大致就是的TwitterCoverScrollView 使用方法介绍。

TwitterCoverScrollView 源码分析。

TwitterCoverScrollView 实现原理和TwitterCover-Android 有比较大的区别,视图不再试通过addview来添加,而是遍历已有视图精准定位,这也是为什么对视图布局的前三层有要求的原因。


image.png

1、onlayout实现遍历子view找到FrameLayout,读取原始高度。
2、islayout 重写返回false。为什么要返回false可以看requestLayout 方法实现和介绍。
3、ontuchevent监听MotionEvent.ACTION_UP 动作实现释放回弹效果。
4、OnRefreshListener 接口实现下拉监听。

/**
 * Created by xsl on 2017/8/28.
 * 自定义srcollview实现 TwitterCover 效果
 * 增加下拉动作监听。
 */
public class TwitterCoverScrollView extends ScrollView {

    private Context mContext;
    /**
     * 需要显示动画的控件容器
     */
    private FrameLayout mFrameLayout;
    /**
     * 显示的背景图片
     */
    private ImageView mImageView;
    /**
     * 能向下拉的最高倍数
     */
    private double highMultiple = 1.6;
    /**
     * 是否已记录原始高度
     */
    private boolean isRecord = false;
    /**
     * FrameLayout 原始高度
     */
    private int fHeight;
    /**
     * FrameLayout实时高度
     */
    private int cHeight;
    /**
     * 是否正在刷新
     */
    private boolean isOnRefesh = false;

    public TwitterCoverScrollView(Context context) {
        this(context,null);
        this.mContext = context;
    }

    public TwitterCoverScrollView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
        this.mContext = context;
    }

    public TwitterCoverScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        setOverScrollMode(OVER_SCROLL_NEVER);
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        int childCount = getChildCount();
        for (int i=0;i<childCount;i++){
            View childView = getChildAt(i);
            if (childView instanceof LinearLayout){
                int count = ((LinearLayout) childView).getChildCount();
                for (int k=0;k<count;k++){
                    View view = ((LinearLayout) childView).getChildAt(k);
                    if (view instanceof FrameLayout) {
                        mFrameLayout = (FrameLayout) view;
//                        int count1 = ((FrameLayout) view).getChildCount();
//                        for (int j=0;j<count1;j++) {
//                            View view1 = ((FrameLayout) view).getChildAt(k);
//                            if (view1 instanceof FrameLayout) {
//                               mImageView = (ImageView) view1;
//                            }
//                        }
                    }
                }
            }
        }
        if (!isRecord) {
            isRecord = true;
            fHeight = mFrameLayout.getMeasuredHeight();
        }
        cHeight = mFrameLayout.getMeasuredHeight();
    }

    /**
     * requestLayout
     * @return
     */
    @Override
    public boolean isInLayout() {
        return false;
    }


    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
        //从顶部超过边界向下拉
        if (deltaY<0 && cHeight < fHeight*highMultiple && isTouchEvent){
            mFrameLayout.getLayoutParams().height =  cHeight - deltaY/2;
            mFrameLayout.requestLayout();
            //下拉等于50dp这个点才会触发刷新动作,避免重复刷新
            if (cHeight-fHeight>= dip2px(mContext,50) && !isOnRefesh) {
                isOnRefesh = true;
                onRefreshListener.onRefresh();
            }
        }
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }


    /**
     * 触摸手指抬起监听实现回弹动画
     * @param ev
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_UP://抬起
                if (fHeight < mFrameLayout.getHeight()) {
                    isOnRefesh = false;
                    ReleaseAnimimation animation = new ReleaseAnimimation(mFrameLayout, fHeight);
                    animation.setDuration(300);
                    mFrameLayout.startAnimation(animation);
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 回弹动画
     */
    public class ReleaseAnimimation extends Animation {
        final View mView;
        final int mTargetHeight;
        final int mExtraHeight;

        protected ReleaseAnimimation(View view, int targetHeight) {
            mView = view;
            mTargetHeight = targetHeight;
            mExtraHeight = mTargetHeight - view.getHeight();
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            mView.getLayoutParams().height = (int) (mTargetHeight - mExtraHeight * (1 - interpolatedTime));
            mView.requestLayout();
        }
    }


    /**
     * 下拉监听接口
     */
    public interface OnRefreshListener{
        void onRefresh();
    }

    /**
     * 设置下拉监听
     */
    OnRefreshListener onRefreshListener;
    public void setOnRefreshListener(OnRefreshListener onRefreshListener){
        this.onRefreshListener = onRefreshListener;
    }

}

如有建议或意见,欢迎底下评论区交流学习,共同进步。

上一篇 下一篇

猜你喜欢

热点阅读