精华内容
下载资源
问答
  • 本文给大家分享android开发中RecyclerView模仿探探左右滑动布局功能,非常不错,具有参考借鉴价值,需要的朋友参考下
  • 主要为大家详细介绍了android实现上下左右滑动的界面布局,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • Android实现九宫格横向左右滑动发布时间:2020-09-29 03:56:38来源:脚本之家阅读:112作者:zuo_er_lyf项目中大多都会有很多的分类,且左右滑动,如美团首页(下图):不难发现包含2部分内容:1.左右滑动的页面,2....

    Android实现九宫格横向左右滑动

    发布时间:2020-09-29 03:56:38

    来源:脚本之家

    阅读:112

    作者:zuo_er_lyf

    项目中大多都会有很多的分类,且左右滑动,如美团首页(下图):

    9981cbb331ea52442d82bda7f62369c3.png

    不难发现包含2部分内容:1.左右滑动的页面,2.指示器。

    大度一般都会想到,viewPager+GridView,这里介绍另外的的一种方法,也做下记录;

    GridViewPager+MagicIndicator(万能指示器)

    一、引入build.gradle

    compile 'com.yhy:gvp:1.1.0'

    compile 'com.github.hackware1993:MagicIndicator:1.5.0'

    如果报错,在项目build.gradle中加入:

    repositories {

    ...

    maven {

    url "https://jitpack.io"

    }

    }

    二、布局代码

    android:gravity="center_horizontal"

    android:layout_gravity="center_horizontal"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:orientation="vertical">

    android:paddingLeft="15dp"

    android:paddingRight="15dp"

    android:id="@+id/grid_viewpager"

    android:layout_width="match_parent"

    android:layout_height="150dp"

    app:num_columns="5"

    app:page_size="10">

    android:id="@+id/indicator_container"

    android:layout_width="wrap_content"

    android:layout_height="30dp">

    属性说明:

    be524655112db633bc9a2fa86ec909a0.png

    三、关键代码:

    @InjectView(R.id.grid_viewpager)

    GridViewPager gridViewpager;

    @InjectView(R.id.indicator_container)

    MagicIndicator indicatorContainer; 使用ButterKnife这里不多介绍

    indexTypeAdapter=new IndexTypeAdapter(getActivity(),R.layout.item_index_type,typeDatas);//页面内容适配器

    gridViewpager.setGVPAdapter(indexTypeAdapter);

    Log.i("datas",(int) Math.ceil(typeDatas.size() / 10)+"");

    CommonNavigator commonNavigator = new CommonNavigator(context);//指示器

    commonNavigator.setAdapter(new CommonNavigatorAdapter() {

    @Override

    public int getCount() {

    int num=typeDatas.size()/10;

    if(typeDatas.size() % 10>0){

    num++;

    }

    return typeDatas==null?0:num;

    }

    @Override

    public IPagerTitleView getTitleView(Context mContext, final int i) {

    CommonPagerTitleView commonPagerTitleView = new CommonPagerTitleView(context);

    View view=View.inflate(context,R.layout.single_image_layout,null);

    final ImageView iv_image=view.findViewById(R.id.iv_image);

    iv_image.setImageResource(R.drawable.point_unfocused);

    commonPagerTitleView.setContentView(view);//指示器引入外部布局,可知指示器内容可根据需求设置,多样化

    commonPagerTitleView.setOnPagerTitleChangeListener(new CommonPagerTitleView.OnPagerTitleChangeListener() {

    @Override

    public void onSelected(int i, int i1) {

    iv_image.setImageResource(R.drawable.point_focused);

    }

    @Override

    public void onDeselected(int i, int i1) {

    iv_image.setImageResource(R.drawable.point_unfocused);

    }

    @Override

    public void onLeave(int i, int i1, float v, boolean b) {

    }

    @Override

    public void onEnter(int i, int i1, float v, boolean b) {

    }

    });

    return commonPagerTitleView;

    }

    @Override

    public IPagerIndicator getIndicator(Context context) {

    return null;

    }

    });

    indicatorContainer.setNavigator(commonNavigator);

    ViewPagerHelper.bind(indicatorContainer, gridViewpager);//页面内容与指示器关联

    四、左右滑动页面内容适配器adapter

    public class IndexTypeAdapter extends GVPAdapter {

    private Context context;

    public IndexTypeAdapter(Context context,int layoutResId, @Nullable List data) {

    super(layoutResId, data);

    this.context=context;

    }

    @Override

    public void bind(View item, int position, IndexAllTypeBean.TypeListBean data) {

    CircleImageView iv_image=item.findViewById(R.id.iv_image);

    TextView tv_type_name=item.findViewById(R.id.tv_type_name);

    Picasso.with(context).load(data.getImageUrl()).into(iv_image);

    tv_type_name.setText(data.getName());

    }

    }

    //IndexAllTypeBean.TypeListBean 为数据内容实体类,不做介绍

    五、内容item布局

    android:orientation="vertical"

    android:paddingBottom="5dp"

    android:layout_marginLeft="5dp"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content">

    //自定义圆形图片,可用ImageView 替代

    android:id="@+id/iv_image"

    android:gravity="center_horizontal"

    android:layout_gravity="center_horizontal"

    android:layout_width="40dp"

    android:layout_height="40dp" />

    android:layout_marginTop="5dp"

    android:gravity="center_horizontal"

    android:layout_gravity="center_horizontal"

    android:id="@+id/tv_type_name"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content" />

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持亿速云。

    展开全文
  • 站在巨人的肩膀上,才能走得更远.参考文章:http://blog.csdn.net/zxt0601/article/details/53730908我在此基础上优化了...看图:1:这种功能, 首先需要自己管理布局 继承 RecyclerView.LayoutManager , 显示自己管理布

    站在巨人的肩膀上,才能走得更远.

    参考文章:http://blog.csdn.net/zxt0601/article/details/53730908

    我在此基础上优化了部分代码, 添加了滑动回调, 可自定义性更强. 并且添加了点击按钮左右滑动的功能.

    据说无图都不敢发文章了.
    看图:

    这里写图片描述


    1:这种功能, 首先需要自己管理布局

    继承 RecyclerView.LayoutManager , 显示自己管理布局, 比如最多显示4个view, 并且都是居中显示.
    底部的View还需要进行缩放,平移操作.

    public class OverLayCardLayoutManager extends RecyclerView.LayoutManager {
        private static final String TAG = "swipecard";
        public static int MAX_SHOW_COUNT = 4;
        public static float SCALE_GAP = 0.05f;
        public static int TRANS_Y_GAP;
    
        public OverLayCardLayoutManager(Context context) {
            //平移时, 需要用到的参考值
            TRANS_Y_GAP = (int) (20 * context.getResources().getDisplayMetrics().density);
        }
    
        @Override
        public RecyclerView.LayoutParams generateDefaultLayoutParams() {
            //必须要实现的方法
            return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
    
        @Override
        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
            //在这个方法中进行View的布局操作.此方法会被调用多次.
            detachAndScrapAttachedViews(recycler);
            int itemCount = getItemCount();
            if (itemCount < 1) {
                return;
            }
            //top-3View的position
            int bottomPosition;
            //边界处理
            if (itemCount < MAX_SHOW_COUNT) {
                bottomPosition = 0;
            } else {
                bottomPosition = itemCount - MAX_SHOW_COUNT;
            }
    
            //从可见的最底层View开始layout,依次层叠上去
            for (int position = bottomPosition; position < itemCount; position++) {
                //1:重recycler的缓存机制中拿到一个View
                View view = recycler.getViewForPosition(position);
                //2:和自定义ViewGroup一样, 需要先addView
                addView(view);
                //3:和自定义ViewGroup一样, 也需要测量View的大小
                measureChildWithMargins(view, 0, 0);
                int widthSpace = getWidth() - getDecoratedMeasuredWidth(view);
                int heightSpace = getHeight() - getDecoratedMeasuredHeight(view);
                //4:和自定义ViewGroup的onLayout一样, 需要layout View.对View进行布局 
                //我们在布局时,将childView居中处理,这里也可以改为只水平居中
                layoutDecoratedWithMargins(view, widthSpace / 2, heightSpace / 2,
                        widthSpace / 2 + getDecoratedMeasuredWidth(view),
                        heightSpace / 2 + getDecoratedMeasuredHeight(view));
                /**
                 * TopView的Scale 为1,translationY 0
                 * 每一级Scale相差0.05f,translationY相差7dp左右
                 *
                 * 观察人人影视的UI,拖动时,topView被拖动,Scale不变,一直为1.
                 * top-1View 的Scale慢慢变化至1,translation也慢慢恢复0
                 * top-2View的Scale慢慢变化至 top-1View的Scale,translation 也慢慢变化只top-1View的translation
                 * top-3View的Scale要变化,translation岿然不动
                 */
    
                //第几层,举例子,count =7, 最后一个TopView(6)是第0层,
                int level = itemCount - position - 1;
    
                //如果不需要缩放平移, 那么下面的代码可以注释掉...
                //除了顶层不需要缩小和位移
                if (level > 0 /*&& level < mShowCount - 1*/) {
                    //每一层都需要X方向的缩小
                    view.setScaleX(1 - SCALE_GAP * level);
                    //前N层,依次向下位移和Y方向的缩小
                    if (level < MAX_SHOW_COUNT - 1) {
                        view.setTranslationY(TRANS_Y_GAP * level);
                        view.setScaleY(1 - SCALE_GAP * level);
                    } else {//第N层在 向下位移和Y方向的缩小的成都与 N-1层保持一致
                        view.setTranslationY(TRANS_Y_GAP * (level - 1));
                        view.setScaleY(1 - SCALE_GAP * (level - 1));
                    }
                }
            }
        }
    }

    2:布局好了之后, 就需要监听鼠标事件了

    谷歌官方提供了一个ItemTouchHelper工具类, 对滑动进行了惨无人道的优越封装, 傻x都能用…
    使用方法: new ItemTouchHelper(callback).attachToRecyclerView(recyclerView);就这么简单,
    接下来的操作, 都在回调callback里面进行.

    public class RenRenCallback extends ItemTouchHelper.SimpleCallback {
    
        private static final String TAG = "RenRen";
        private static final int MAX_ROTATION = 15;
        OnSwipeListener mSwipeListener;
        boolean isSwipeAnim = false;
    
        public RenRenCallback() {
            //第一个参数决定可以拖动排序的方向, 这里由于不需要拖动排序,所以传0
            //第二个参数决定可以支持滑动的方向,这里设置了上下左右都可以滑动.
            super(0, ItemTouchHelper.DOWN | ItemTouchHelper.UP | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
        }
    
        public void setSwipeListener(OnSwipeListener swipeListener) {
            mSwipeListener = swipeListener;
        }
    
        //水平方向是否可以被回收掉的阈值
        public float getThreshold(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            //2016 12 26 考虑 探探垂直上下方向滑动,不删除卡片,这里参照源码写死0.5f
            return recyclerView.getWidth() * /*getSwipeThreshold(viewHolder)*/ 0.5f;
        }
    
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            //由于不支持滑动排序, 所以不需要处理此方法
            return false;
        }
    
        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
            //当view需要滑动的时候,会回调此方法
            //但是这个方法只是告诉你View需要滑动, 并不是对View和Adapter进行额外的操作,
            //所以, 如果你需要实现滑动删除, 那么需要在此方法中remove item等.
    
            //我们这里需要对滑动过后的View,进行恢复操作. 
            viewHolder.itemView.setRotation(0);//恢复最后一次的旋转状态
            if (mSwipeListener != null) {
                mSwipeListener.onSwipeTo(viewHolder, 0);
            }
            notifyListener(viewHolder.getAdapterPosition(), direction);
        }
    
        private void notifyListener(int position, int direction) {
            Log.w(TAG, "onSwiped: " + position + " " + direction);
            if (mSwipeListener != null) {
                mSwipeListener.onSwiped(position, direction);
            }
        }
    
        @Override
        public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
            //滑动的比例达到多少之后, 视为滑动
            return 0.3f;
        }
    
    
        @Override
        public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            //当你在滑动的过程中, 此方法一直会被回调, 就跟onTouch事件一样...
            //先根据滑动的dx dy 算出现在动画的比例系数fraction
            float swipeValue = (float) Math.sqrt(dX * dX + dY * dY);
            final float threshold = getThreshold(recyclerView, viewHolder);
            float fraction = swipeValue / threshold;
            //边界修正 最大为1
            if (fraction > 1) {
                fraction = 1;
            } else if (fraction < -1) {
                fraction = -1;
            }
            //对每个ChildView进行缩放 位移
            int childCount = recyclerView.getChildCount();
            for (int i = 0; i < childCount; i++) {
                View child = recyclerView.getChildAt(i);
                //第几层,举例子,count =7, 最后一个TopView(6)是第0层,
                int level = childCount - i - 1;
                if (level > 0) {
                    child.setScaleX(1 - SCALE_GAP * level + fraction * SCALE_GAP);
    
                    if (level < MAX_SHOW_COUNT - 1) {
                        child.setScaleY(1 - SCALE_GAP * level + fraction * SCALE_GAP);
                        child.setTranslationY(TRANS_Y_GAP * level - fraction * TRANS_Y_GAP);
                    } else {
                        //child.setTranslationY((float) (mTranslationYGap * (level - 1) - fraction * mTranslationYGap));
                    }
                } else {
                    //最上层
                    //rotate
                    if (dX < -50) {
                        child.setRotation(-fraction * MAX_ROTATION);
                    } else if (dX > 50) {
                        child.setRotation(fraction * MAX_ROTATION);
                    } else {
                        child.setRotation(0);
                    }
    
                    if (mSwipeListener != null) {
                        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                        final int adapterPosition = params.getViewAdapterPosition();
                        mSwipeListener.onSwipeTo(recyclerView.findViewHolderForAdapterPosition(adapterPosition), dX);
                    }
                }
            }
        }
    
        //扩展实现:点击按钮实现左滑效果
        public void toLeft(RecyclerView recyclerView) {
            if (check(recyclerView)) {
                animTo(recyclerView, false);
            }
        }
    
        //扩展实现:点击按钮实现右滑效果
        public void toRight(RecyclerView recyclerView) {
            if (check(recyclerView)) {
                animTo(recyclerView, true);
            }
        }
    
        private void animTo(final RecyclerView recyclerView, boolean right) {
            final int position = recyclerView.getAdapter().getItemCount() - 1;
            final View view = recyclerView.findViewHolderForAdapterPosition(position).itemView;
    
            TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
                    Animation.RELATIVE_TO_SELF, right ? 1f : -1f,
                    Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 1.3f);
            translateAnimation.setFillAfter(true);
            translateAnimation.setDuration(300);
            translateAnimation.setInterpolator(new DecelerateInterpolator());
            translateAnimation.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
    
                }
    
                @Override
                public void onAnimationEnd(Animation animation) {
                    isSwipeAnim = false;
                    recyclerView.removeView(view);
                    notifyListener(position,
                            x > view.getMeasuredWidth() / 2
                                    ?
                                    ItemTouchHelper.RIGHT : ItemTouchHelper.LEFT);
                }
    
                @Override
                public void onAnimationRepeat(Animation animation) {
    
                }
            });
            view.startAnimation(translateAnimation);
        }
    
        private boolean check(RecyclerView recyclerView) {
            if (isSwipeAnim) {
                return false;
            }
            if (recyclerView == null || recyclerView.getAdapter() == null) {
                return false;
            }
            if (recyclerView.getAdapter().getItemCount() == 0) {
                return false;
            }
            isSwipeAnim = true;
            return true;
        }
    
        public interface OnSwipeListener {
    
            /**
             * @param direction {@link ItemTouchHelper#LEFT} / {@link ItemTouchHelper#RIGHT}
             *                  {@link ItemTouchHelper#UP} or {@link ItemTouchHelper#DOWN}).
             */
            void onSwiped(int adapterPosition, int direction);
    
            /**
             * 最上层View滑动时回调.
             *
             * @param viewHolder 最上层的ViewHolder
             * @param offset     距离原始位置的偏移量
             */
            void onSwipeTo(RecyclerView.ViewHolder viewHolder, float offset);
        }
    
        public static class SimpleSwipeCallback implements OnSwipeListener {
    
            /**
             * {@inheritDoc}
             */
            @Override
            public void onSwiped(int adapterPosition, int direction) {
    
            }
    
            /**
             * {@inheritDoc}
             */
            @Override
            public void onSwipeTo(RecyclerView.ViewHolder viewHolder, float offset) {
    
            }
        }
    }

    看起来不难, 但是真正做的时候, 要处理的地方很多,

    并且有些地方要思考很久, 才能实现效果.

    总之,做了你才会发现1+1=2的魅力, just do it.

    开源地址: https://github.com/angcyo/RecyclerLayoutManager


    至此: 文章就结束了,如有疑问: QQ群 Android:274306954 Swift:399799363 欢迎您的加入.

    展开全文
  • android 上下滑动布局

    2018-02-28 03:47:59
    这个是我目前的布局... 还有一些东西需要添加.. 但是又放不下 需要做一个可以上下滑动的页面 然后试了scrolview 和系统自带的scroling_active 都是不能放入button.否则就直接闪退.. 我是个做web的 这公司要求做app...
  • Android VegaScroll滚动布局,会随着手指滑动实现收缩隐藏与放大显示,并伴随recycler的回收与复用。代码比较简单粗暴,使用自定义的LayoutManger,内置SnapHelper。
  • 在开发项目的时候,有时候会遇到一些比较复杂的页面,需要多个不同的列表或者滑动布局、甚至是WebView,组成一个完整的页面。要实现这样一个复杂的页面,在以前我们可能会通过布局嵌套的方式,在一个大的ScrollView下...

    在开发项目的时候,有时候会遇到一些比较复杂的页面,需要多个不同的列表或者滑动布局、甚至是WebView,组成一个完整的页面。要实现这样一个复杂的页面,在以前我们可能会通过布局嵌套的方式,在一个大的ScrollView下嵌套多个RecyclerView、WebView、ScrollView来实现。但是这种嵌套的方式不仅会严重影响布局的性能,而且处理滑动事件的冲突也是一件头疼的事,处理不好会严重影响用户操作的体验。ConsecutiveScrollerLayout正是为了解决这个难题而设计的滑动布局,它可以同时嵌套多个滑动布局(RecyclerView、WebView、ScrollView等)和普通控件(TextView、ImageView、LinearLayou、自定义View等),它把所有的子View看作是一个整体,由ConsecutiveScrollerLayout来统一处理布局的滑动,使得多个滑动布局就像一个整体一样连续滑动,它的效果就像是一个ScrollView一样。而且它支持嵌套所有的View,具有很好的通用性。使用者不需要关心它是如何滑动的,也不需要考虑滑动的冲突问题,并且不用担心它会影响子View的性能。

    下面是对ConsecutiveScrollerLayout的使用介绍和一写注意事项。

    项目地址: ConsecutiveScroller
    ConsecutiveScroller的设计思路和源码分析:Android可持续滑动布局:ConsecutiveScrollerLayout

    效果图

    sample.gif sticky.gif

    引入依赖

    在Project的build.gradle在添加以下代码

    allprojects {
          repositories {
             ...
             maven { url 'https://jitpack.io' }
          }
       }
    

    在Module的build.gradle在添加以下代码

    implementation 'com.github.donkingliang:ConsecutiveScroller:2.6.2'
    

    注意: 如果你准备使用这个库,请务必认真阅读下面的文档。它能让你了解ConsecutiveScrollerLayout可以实现的功能,以及避免不必要的错误。

    基本使用

    ConsecutiveScrollerLayout的使用非常简单,把需要滑动的布局作为ConsecutiveScrollerLayout的直接子View即可。

    <?xml version="1.0" encoding="utf-8"?>
    <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/scrollerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical">
    
        <WebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:background="@android:color/holo_red_dark"
            android:gravity="center"
            android:orientation="vertical">
    
        </LinearLayout>
    
        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        </androidx.core.widget.NestedScrollView>
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitXY"
            android:src="@drawable/temp" />
    
        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        </ScrollView>
    
      <!--  可以嵌套ConsecutiveScrollerLayout  -->
      <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/design_default_color_primary">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text=""
                android:textColor="@android:color/black"
                android:textSize="18sp" />
    
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerView3"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
    
        </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
    </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
    

    关于margin

    ConsecutiveScrollerLayout支持左右margin,但是不支持上下margin,子View间的间距可以通过Space设置。

    <?xml version="1.0" encoding="utf-8"?>
    <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/scrollerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical">
    
        <!--  使用Space设置上下边距   -->
        <Space
            android:layout_width="0dp"
            android:layout_height="20dp" />
    
        <!--  ConsecutiveScrollerLayout支持左右margin,但是不支持上下margin   -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:background="@android:color/holo_red_dark"
            android:gravity="center"
            android:orientation="vertical">
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="LinearLayout"
                android:textColor="@android:color/black"
                android:textSize="18sp" />
    
        </LinearLayout>
    
        <!--  使用Space设置上下边距   -->
        <Space
            android:layout_width="0dp"
            android:layout_height="20dp" />
    
    </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
    
    

    布局对齐方式

    ConsecutiveScrollerLayout的布局方式类似于垂直的LinearLayout,但是它没有gravity和子view layout_gravity属性。ConsecutiveScrollerLayout为它的子view提供了layout_align属性,用于设置子view和父布局的对齐方式。layout_align有三个值:左对齐(LEFT)右对齐(RIGHT)中间对齐(CENTER)

    <?xml version="1.0" encoding="utf-8"?>
    <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"                                                                    xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/scrollerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical">
      
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:padding="10dp"
            android:text="吸顶View - 1"
            android:textColor="@android:color/black"
            android:textSize="18sp"
            app:layout_isSticky="true"
            app:layout_align="LEFT"/> // 对齐方式
      
    </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
    

    嵌套Fragment

    要想把一个Fragment嵌套在ConsecutiveScrollerLayout里。通常我们需要一个布局容器来承载我们的Fragment,或者直接把Fragment写在activity的布局里。如果Fragment是垂直滑动的,那么承载Fragment的容器需要是ConsecutiveScrollerLayout,以及Fragment的根布局也需要是垂直滑动的。我们推荐使用ConsecutiveScrollerLayout或者其他可垂直滑动的控件(比如:RecyclerView、NestedScrollView)作为Fragment的根布局。如果你的Fragment不是垂直滑动的,则可以忽略这个限制。

    <?xml version="1.0" encoding="utf-8"?>
    <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/scrollerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical">
    
        <!--  承载Fragment的容器  -->
        <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout
            android:id="@+id/fragment_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
      
    <!--  MyFragment的根布局是垂直滑动控件  -->
       <fragment
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:name="com.donkingliang.consecutivescrollerdemo.MyFragment"/>
    
    </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
    

    布局吸顶

    要实现布局的吸顶效果,在以前,我们可能会写两个一摸一样的布局,一个隐藏在顶部,一个嵌套在ScrollView下,通过监听ScrollView的滑动来显示和隐藏顶部的布局。这种方式实现起来既麻烦也不优雅。ConsecutiveScrollerLayout内部实现了子View吸附顶部的功能,只要设置一个属性,就可以实现吸顶功能。而且支持设置多个子View吸顶,后面的View要吸顶时会把前面的吸顶View推出屏幕。

    <?xml version="1.0" encoding="utf-8"?>
    <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/scrollerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical">
    
      <!-- 设置app:layout_isSticky="true"就可以使View吸顶 -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:padding="10dp"
            android:text="吸顶View - 1"
            android:textColor="@android:color/black"
            android:textSize="18sp"
            app:layout_isSticky="true" />
    
        <WebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:orientation="vertical"
            app:layout_isSticky="true">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="吸顶View - 2 我是个LinearLayout"
                android:textColor="@android:color/black"
                android:textSize="18sp" />
    
        </LinearLayout>
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:padding="10dp"
            android:text="吸顶View - 3"
            android:textColor="@android:color/black"
            android:textSize="18sp"
            app:layout_isSticky="true" />
    
        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        </androidx.core.widget.NestedScrollView>
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:padding="10dp"
            android:text="吸顶View - 4"
            android:textColor="@android:color/black"
            android:textSize="18sp"
            app:layout_isSticky="true" />
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
    

    常驻吸顶

    如果你不希望吸顶的view被后面的吸顶view顶出屏幕,而且多个吸顶view排列吸附在顶部,你可以设置常驻吸顶模式:app:isPermanent=“true”

    <?xml version="1.0" encoding="utf-8"?>
    <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/scrollerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:isPermanent="true"  // 常驻吸顶
        android:scrollbars="vertical">
    
    </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
    

    关于吸顶功能的其他方法

    // 设置吸顶到顶部的距离,在距离顶部一定距离时开始悬停吸顶
    scrollerLayout.setStickyOffset(50);
    
    // 监听吸顶变化(普通模式)
    scrollerLayout.setOnStickyChangeListener(OnStickyChangeListener);
    // 监听吸顶变化(常驻模式)
    scrollerLayout.setOnPermanentStickyChangeListener(OnPermanentStickyChangeListener);
    // 获取当前吸顶view(普通模式)
    scrollerLayout.getCurrentStickyView(); 
    // 获取当前吸顶view(常驻模式)
    scrollerLayout.getCurrentStickyViews();
    

    局部滑动

    ConsecutiveScrollerLayout将所有的子View视作一个整体,由它统一处理页面的滑动事件,所以它默认会拦截可滑动的子View的滑动事件,由自己来分发处理。并且会追踪用户的手指滑动事件,计算调整ConsecutiveScrollerLayout滑动偏移。如果你希望某个子View自己处理自己的滑动事件,可以通过设置layout_isConsecutive属性来告诉父View不要拦截它的滑动事件,这样就可以实现在这个View自己的高度内滑动自己的内容,而不会作为ConsecutiveScrollerLayout的一部分来处理。

    <?xml version="1.0" encoding="utf-8"?>
    <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/scrollerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical">
      
    <!--设置app:layout_isConsecutive="false"使父布局不拦截滑动事件-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_isConsecutive="false">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="下面的红色区域是个RecyclerView,它在自己的范围内单独滑动。"
                android:textColor="@android:color/black"
                android:textSize="18sp"
                app:layout_isSticky="true" />
    
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerView1"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:layout_marginLeft="50dp"
                android:layout_marginRight="50dp"
                android:layout_marginBottom="30dp"
                android:background="@android:color/holo_red_dark"/>
    
        </LinearLayout>
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="下面的是个NestedScrollView,它在自己的范围内单独滑动。"
            android:textColor="@android:color/black"
            android:textSize="18sp" />
    
        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="250dp"
            app:layout_isConsecutive="false">
    
        </androidx.core.widget.NestedScrollView>
    
    </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
    

    在这个例子中NestedScrollView希望在自己的高度里滑动自己的内容,而不是跟随ConsecutiveScrollerLayout滑动,只要给它设置layout_isConsecutive="false"就可以了。而LinearLayout虽然不是滑动布局,但是在下面嵌套了个滑动布局RecyclerView,所以它也需要设置layout_isConsecutive=“false”。

    ConsecutiveScrollerLayout支持NestedScrolling机制,如果你的局部滑动的view实现了NestedScrollingChild接口(如:RecyclerView、NestedScrollView等),它滑动完成后会把滑动事件交给父布局。如果你不想你的子view或它的下级view与父布局嵌套滑动,可以给子view设置app:layout_isNestedScroll=“false”。它可以禁止子view与ConsecutiveScrollerLayout的嵌套滑动

    滑动子view的下级view

    ConsecutiveScrollerLayout默认情况下只会处理它的直接子view的滑动,但有时候需要滑动的布局可能不是ConsecutiveScrollerLayout的直接子view,而是子view所嵌套的下级view。比如ConsecutiveScrollerLayout嵌套FrameLayout,FrameLayout嵌套ScrollView,我们希望ConsecutiveScrollerLayout也能正常处理ScrollView的滑动。为了支持这种需求,ConsecutiveScroller提供了一个接口:IConsecutiveScroller。子view实现IConsecutiveScroller接口,并通过实现接口方法告诉ConsecutiveScrollerLayout需要滑动的下级view,ConsecutiveScrollerLayout就能正确地处理它的滑动事件。IConsecutiveScroller需要实现两个方法:

        /**
         * 返回当前需要滑动的下级view。在一个时间点里只能有一个view可以滑动。
         */
        View getCurrentScrollerView();
    
        /**
         * 返回所有可以滑动的子view。由于ConsecutiveScrollerLayout允许它的子view包含多个可滑动的子view,所以返回一个view列表。
         */
        List<View> getScrolledViews();
    

    在前面提到的例子中,我们可以这样实现:

    public class MyFrameLayout extends FrameLayout implements IConsecutiveScroller {
    
        @Override
        public View getCurrentScrollerView() {
            // 返回需要滑动的ScrollView
            return getChildAt(0);
        }
    
        @Override
        public List<View> getScrolledViews() {
            // 返回需要滑动的ScrollView
            List<View> views = new ArrayList<>();
            views.add(getChildAt(0));
            return views;
        }
    }
    
    <?xml version="1.0" encoding="utf-8"?>
    <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/scrollerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical">
    
        <com.donkingliang.consecutivescrollerdemo.widget.MyFrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <ScrollView
                android:layout_width="match_parent"
                android:layout_height="match_parent">
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">
    
                </LinearLayout>
            </ScrollView>
        </com.donkingliang.consecutivescrollerdemo.widget.MyFrameLayout>
    </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
    

    这样ConsecutiveScrollerLayout就能正确地处理ScrollView的滑动。这是一个简单的例子,在实际的需求中,我们一般不需要这样做。

    注意:

    1、getCurrentScrollerView()和getScrolledViews()必须正确地返回需要滑动的view,这些view可以是经过多层嵌套的,不一定是直接子view。所以使用者应该按照自己的实际场景去实现者两个方法。

    2、滑动的控件应该跟嵌套它的子view的高度保持一致,也就是说滑动的控件高度应该是match_parent,并且包裹它的子view和它本身都不要设置上下边距(margin和ppadding)。宽度没有这个限制。

    对ViewPager的支持

    如果你的ViewPager承载的子布局(或Fragment)不是可以垂直滑动的,那么使用普通的ViewPager即可。如果是可以垂直滑动的,那么你的ViewPager需要实现IConsecutiveScroller接口,并返回需要滑动的view对象。框架里提供了一个实现了IConsecutiveScroller接口自定义控件:ConsecutiveViewPager。使用这个控件,然后你的ConsecutiveViewPager的子view(或Fragment的根布局)是可垂直滑动的view,如:RecyclerView、NestedScrollView、ConsecutiveScrollerLayout即可。这样你的ViewPager就能正确地跟ConsecutiveScrollerLayout一起滑动了。

    <?xml version="1.0" encoding="utf-8"?>
    <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/scrollerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical">
    
        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            app:tabGravity="fill"
            app:tabIndicatorColor="@color/colorPrimary"
            app:tabIndicatorHeight="3dp"
            app:tabMode="scrollable"
            app:tabSelectedTextColor="@color/colorPrimary" />
    
        <com.donkingliang.consecutivescroller.ConsecutiveViewPager
            android:id="@+id/viewPager"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
    

    布局吸顶时会覆盖在下面的布局的上面,有时候我们希望TabLayout吸顶悬浮在顶部,但是不希望它覆盖遮挡ViewPager的内容。ConsecutiveViewPager提供了setAdjustHeight调整自己的布局高度,让自己不被TabLayout覆盖。注意:只有ConsecutiveScrollerLayout是ConsecutiveScrollerLayout的最低部时才能这样做。

    // 保证能获取到tabLayout的高度
    tabLayout.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setAdjustHeight(tabLayout.getHeight());
        }
    });
    

    其他注意事项

    1、WebView在加载的过程中如果滑动的布局,可能会导致WebView与其他View在显示上断层,使用下面的方法一定程度上可以避免这个问题。

    webView.setWebChromeClient(new WebChromeClient() {
                @Override
                public void onProgressChanged(WebView view, int newProgress) {
                    super.onProgressChanged(view, newProgress);
                    scrollerLayout.checkLayoutChange();
                }
            });
    

    2、SmartRefreshLayout和SwipeRefreshLayout等刷新控件可以嵌套ConsecutiveScrollerLayout实现下拉刷新功能,但是ConsecutiveScrollerLayout内部嵌套它们来刷新子view,因为子view时ConsecutiveScrollerLayout滑动内容等一部分。除非你给SmartRefreshLayout或者SwipeRefreshLayout设置app:layout_isConsecutive=“false”。

    3、继承AbsListView的布局(ListView、GridView等),在滑动上可能会与用户的手指滑动不同步,推荐使用RecyclerView代替。

    4、ConsecutiveScroller的minSdkVersion是19,如果你的项目支持19以下,可以设置:

    <uses-sdk tools:overrideLibrary="com.donkingliang.consecutivescroller"/>
    

    但是不要在minSdkVersion小于19的项目使用AbsListView的子类,因为ConsecutiveScrollerLayout使用了只有19以上才有的AbsListView API。

    5、使用ConsecutiveScrollerLayout提供的setOnVerticalScrollChangeListener()方法监听布局的滑动事件。View所提供的setOnScrollChangeListener()方法已无效。

    6、通过getOwnScrollY()方法获取ConsecutiveScrollerLayout的垂直滑动距离,View的getScrollY()方法获取的不是ConsecutiveScrollerLayout的整体滑动距离。

    展开全文
  • ListView对应多个布局的实现方式。ListView滚动布局错误的解决方案,博客中对于的介绍博文
  • 主要为大家详细介绍了Android仿抖音上下滑动布局,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 前段时间我在GitHub上开源了一个Android滑动布局ConsecutiveScrollerLayout,主要是为了解决布局嵌套滑动和滑动冲突的问题。另外还写了两篇文章用于介绍ConsecutiveScrollerLayout的实现原理和使用。这篇文章是对...

    前段时间我在GitHub上开源了一个Android滑动布局ConsecutiveScrollerLayout,主要是为了解决布局嵌套滑动和滑动冲突的问题。另外还写了两篇文章用于介绍ConsecutiveScrollerLayout的实现原理和使用。这篇文章是对讲解ConsecutiveScrollerLayout实现原理的补充,主要是讲解ConsecutiveScrollerLayout实现布局吸顶的原理。没有看过我前面那两篇文章的朋友可以去看一下。

    Android可持续滑动布局:ConsecutiveScrollerLayout

    Android持续滑动布局ConsecutiveScrollerLayout的使用

    虽然我写ConsecutiveScrollerLayout是为了解决复杂布局的滑动问题,不过布局滑动吸顶的需求在平常的开发中也很常见,既然我已经实现了一个滑动布局,所以就干脆给它加上吸顶的功能了。

    以前我们实现布局滑动吸顶的功能,最常用的方法就是在FrameLayout里嵌套一个隐藏的、放在顶部的布局,和一个ScrollView,ScrollView里再放一个跟隐藏的顶部布局一摸一样的布局,然后监听ScrollView的滑动,如果ScrollView里需要吸顶的布局滑出屏蔽,就把顶部隐藏的布局显示出来。这种方法实现起来既麻烦也不优雅。ConsecutiveScrollerLayout通过计算滑动位置和给需要吸顶的view设置y轴偏移量,让需要吸顶的布局在滑出屏幕时悬浮在布局的顶部。只要给布局设置一个属性,就可以实现吸顶的功能。

    ConsecutiveScrollerLayout吸顶悬浮功能的主要实现是在resetSticky()方法。当布局滑动或者发生变化的时候会调用这个方法。

        private void resetSticky() {
          // 吸顶功能使用了Android 5.0才支持的API,所以Android 5.0才能吸顶
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
              // 获取需要吸顶的子view,ConsecutiveScrollerLayout支持设置多个吸顶的view。
                List<View> children = getStickyChildren();
                if (!children.isEmpty()) {
                    int count = children.size();
    
                    // 重置吸顶布局时,先让所有的View恢复原来的状态
                    for (int i = 0; i < count; i++) {
                        View child = children.get(i);
                        child.setTranslationY(0);
                        child.setTranslationZ(0);
                    }
    
                    // 需要吸顶的View
                    View stickyView = null;
                    // 下一个需要吸顶的View
                    View nextStickyView = null;
    
                    // 找到需要吸顶的View
                    for (int i = count - 1; i >= 0; i--) {
                        View child = children.get(i);
                        if (child.getTop() <= getScrollY()) {
                            stickyView = child;
                            if (i != count - 1) {
                                nextStickyView = children.get(i + 1);
                            }
                            break;
                        }
                    }
    
                    if (stickyView != null) {
                        int offset = 0;
                        if (nextStickyView != null) {
                          // 如果nextStickyView不为空,计算布局吸顶时需要设置的偏移量。
                          // 因为下一个吸顶的view顶到了当前吸顶的view,需要把当前的view顶出屏幕
                            offset = Math.max(0, stickyView.getHeight() - (nextStickyView.getTop() - getScrollY()));
                        }
                        stickyChild(stickyView, offset);
                    }
                }
            }
        }
    
        private void stickyChild(View child, int offset) {
          // 设置吸顶view的y轴偏移量,让它悬浮在顶部
            child.setY(getScrollY() - offset);
          // 设置吸顶view的translationZ为1
            child.setTranslationZ(1);
        }
    
        /**
         * 返回所有的吸顶子View(非GONE)
         */
        private List<View> getStickyChildren() {
            List<View> children = new ArrayList<>();
            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                if (child.getVisibility() != GONE && isStickyChild(child)) {
                    children.add(child);
                }
            }
            return children;
        }
    
    		/**
         * 是否是需要吸顶的View
         */
        private boolean isStickyChild(View child) {
            ViewGroup.LayoutParams lp = child.getLayoutParams();
            if (lp instanceof LayoutParams) {
              // 是否需要吸顶
                return ((LayoutParams) lp).isSticky;
            }
            return false;
        }
    

    这里使用了一个isSticky的自定义LayoutParams属性,这个属性用于表示子view是否需要吸顶,如果ConsecutiveScrollerLayout的子view设置了app:layout_isSticky=“true”,表示这个view需要吸顶。

    吸顶的原理是通过ConsecutiveScrollerLayout的scrollY找到需要吸顶的view和下一个需要吸顶的view,之所以要找到下一个吸顶的view,是因为布局继续向上滑动时,下一个吸顶的view需要把当前吸顶的view顶出屏幕,所以要计算它们之间的偏移量。吸顶的view通过setY()设置y轴偏移量,让它停留在顶部。最后我给吸顶的view设置了translationZ为1,这是由于Android布局的显示层级,两个view重叠时,后添加的会将先添加的覆盖。为了让吸顶的view不被后面的view覆盖掉,所以需要给它设置一下z轴,z越大的view,显示的层级越高。view的z等于translationZ+elevation。除了一些特殊的控件,Android几乎所有的view的这两个值都是0。

    下面是吸顶功能的使用和效果。

    <?xml version="1.0" encoding="utf-8"?>
    <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/scrollerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical">
    
      <!-- 设置app:layout_isSticky="true"就可以使View吸顶 -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:padding="10dp"
            android:text="吸顶View - 1"
            android:textColor="@android:color/black"
            android:textSize="18sp"
            app:layout_isSticky="true" />
    
        <WebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:orientation="vertical"
            app:layout_isSticky="true">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="10dp"
                android:text="吸顶View - 2 我是个LinearLayout"
                android:textColor="@android:color/black"
                android:textSize="18sp" />
    
        </LinearLayout>
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:padding="10dp"
            android:text="吸顶View - 3"
            android:textColor="@android:color/black"
            android:textSize="18sp"
            app:layout_isSticky="true" />
    
        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        </androidx.core.widget.NestedScrollView>
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:padding="10dp"
            android:text="吸顶View - 4"
            android:textColor="@android:color/black"
            android:textSize="18sp"
            app:layout_isSticky="true" />
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vkjEv4TI-1585144985069)(https://github.com/donkingliang/ConsecutiveScroller/blob/master/image/sticky.gif)]

    项目地址: ConsecutiveScroller

    展开全文
  • android 可以上下左右滑动的界面布局

    千次阅读 2019-03-06 11:21:37
    1.我使用的是ScrollView嵌套HorizontalScrollView让ScrollView负责上下滑动HorizontalScrollView负责左右滑动 2.以下代码提供了思路和完成手段,请根据具体业务去进行修改,我试过使用recyclerview进行自定义,发现...
  • android viewPager fragment 左右滑动布局 替代tabhost布局
  • Android 滑动布局

    千次阅读 2019-03-26 14:27:00
    2.SlidingPaneLayout V4包侧滑布局(两个子布局,第1个为侧滑布局,第2个为主布局) 3.DrawerLayout 抽屉布局 4.SwipeRefreshLayout布局 转载于:...
  • 实现自定义左右滚动的信息块 这种左右滚动的内容,在很多常见的o2o 什么p2p c2c o2c c2o p2c c2p 各种软件中比较常见,譬如蘑菇街的 这种布局就是将LinearLayout嵌套在HorizontalScrollView中,实现的原理就好像...
  • Android滑动的SwipeLayout布局
  • 主要为大家详细介绍了Android可自定义垂直循环滚动布局,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • android之View篇6————仿陌陌卡片左右滑动选择控件 一.目录 android之View篇6————仿陌陌卡片左右滑动选择控件 一.目录 二.效果图 三.业务需求梳理 四.思路分析 1. 新建TinderCardView类,并继承...
  • 左右滑动布局 导入 Android Studio-New-Import Module 添加依赖 build.gradle dependencies { implementation project(':SwipeLayoutLibrary-master') } settings.gradle include ':app', ':SwipeLayoutLibrary...
  • android 图片左右滑动

    2015-05-27 15:19:50
    android 多张图片在布局的某个区域左右定时滑动显示,点击后按照手势方向滑动图片
  • code小生 一个专注大前端领域的技术平台公众号回复Android加入安卓技术群作者:donkingliang链接:https://www.jianshu.com/p/0783b0a37...
  • ConsecutiveScrollerLayout是我在GitHub开源的一个Android自定义滑动布局,它可以让多个滑动布局和普通控件在界面上像一个整体一样连续顺畅地滑动。 试想我们有这样一个需求,在一个界面上有轮播图、像九宫格一样的...
  • android滚动球面布局

    2019-01-02 15:54:43
    在某网站上看到一个效果,就是关键字的布局呈球状排布,并可以根据鼠标的移动而旋转。于是也想在Android实现同样的效果,顺便封装成通用的控件,说不定以后就可以用到了。 说干就干,大家先看看我的实现效果,...
  • android 横向可滑动布局

    千次阅读 2017-03-21 14:50:25
    使用的是RecyclerView(其实我感觉跟listview差不多),首先构造适配器: ... * 横向和滑动适配器 */ public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerHolder> { p...
  • 本文实例为大家分享了Android HorizontalScrollView左右滑动的具体代码,供大家参考,具体内容如下 效果图 一.什么是HorizontalScrollView HorizontalScrollView实际上是一个FrameLayout ,这意味着你只能在它...
  • 这种左右滚动的菜单,在很多常见的o2o 什么p2p c2c o2c c2o p2c c2p 各种软件中比较常见,譬如美团的 这种布局就是将GridView嵌套了ViewPager , ViewPager中的数据模型适配器就是View,也就是子类 GridView, ...
  • 左右滑动布局

    2017-05-10 17:19:29
    // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item mScroller.startScroll(mParentView.getScrollX(), 0, delta-1, 0, Math.abs(delta)); postInvalidate(); } ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 98,914
精华内容 39,565
关键字:

安卓左右滑动布局