android 自定义StickyLayout

项目要求实现sticky的效果,涉及:自定义View、事件分发、滚动Scroller效果、加速滚动、计算和定位Sticky

一、效果图:

 

二、自定义LienarLayout实现效果

/**
 * author:白迎宾
 * time:2021/10/9
 * description: StickyLinearLayout
 * 基本思路:
 * 1、继承LinearLayout,实现OnGestureListener使用fling方法检测手势
 * 2、重写onLayout计算LinearLayout里所有的View的高度,计算可滑动的最大值
 * 3、构造方法初始化辅助类(OverScroller实现滑动监听、VelocityTracker滑动速度跟踪器、GestureDetector设置手势检测)
 *    注意:setLongClickable(true); //这里设置长按事件可用,否则dispatchTouchEvent的Down和Up事件都不会执行(也可以采取事件拦截方式)
 * 4、重写onMeasure获取HeadView的高度,并重写计算和适配展示的高度  当前高度+headView的高度
 * 5、重写dispatchTouchEvent,根据事件处理scrollBy滑动操作和加速滑动Scroller.fling()
 * 6、重写computeScroll()获取offset距离,scrollTo到滑动的位置
 * 7、重写scrollBy和scrollTo判断可滑动的值是headView的最大高度和最小高度
 * 8、获取getChildAt(2)就是可滚动的View,是否滑动到了第一条的最顶部 isTop()
 * 9、处理事件分发:根据(mCurY==mHeadHeight||mCurY>mHeadHeight)&&!isTop())
 *   (滑动的Y轴的值和HeadView的高度,以及子ScrollView是否滑动到第一条的最顶部,来确定事件交给谁处理)
 *    如果 headView 隐藏了,那么事件拦截,交给子View(ScrollView)处理
 *    如果 headView 要展示和展示的情况下,交给当前View去滑动展示headView
 * 10、headView是ViewPager,并且ViewPager的子View是ListView这种可以滚动的View,需要在ACTION_DOWN的时候,判断点击位置,并处理拦截状态
 */
public class StickyLinearLayout extends LinearLayout implements GestureDetector.OnGestureListener{

    protected OverScroller mScroller; //滚动辅助类
    protected VelocityTracker mVelocityTracker; //滑动速度跟踪器
    protected GestureDetector mGestureDetector; //手势检测
    private int mMaxVelocity; //触发fling最大滑动速度
    private int mScrollRange; //可滚动最大距离(不包含最后一个子控件marginBottom)

    private float scrollY=0;//纵向滑动时,记录y轴的坐标值
    private float lastScrollY = 0;//纵向滑动时,记录上一次y轴的坐标值

    private View mHeadView;
    private int mHeadHeight;

    private int minY = 0;//minY是Y轴可滑动的最小值,最小默认0,用来实现sticky固定
    private int maxY = 0;//maxY是Y轴可滑动的最大值,默认HeadView的高度,用来实现sticky固定
    private int mCurY;//最后移动的Y的值

    public StickyLinearLayout(Context context) {
        super(context);
        init(context);
    }

    public StickyLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public StickyLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public StickyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    private void init(Context context){
        //这里设置长按事件可用,否则dispatchTouchEvent的Down和Up事件都不会执行(也可以采取事件拦截方式)
        setLongClickable(true);
        mScroller = new OverScroller(context);
        mGestureDetector = new GestureDetector(context, this);
        mMaxVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed) {                                 //当布局发生改变时计算滚动范围
            Rect rect = new Rect();
            getGlobalVisibleRect(rect);
            View lastChild = getChildAt(getChildCount() - 1);
            if (null != lastChild){
                //获取当前View可滚动距离
                mScrollRange = lastChild.getBottom() - (rect.bottom-rect.top);
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //第一个View作为headView,并且测量headView高度
        mHeadView = getChildAt(0);
        measureChildWithMargins(mHeadView, widthMeasureSpec, 0, MeasureSpec.UNSPECIFIED, 0);
        maxY = mHeadView.getMeasuredHeight();
        mHeadHeight = mHeadView.getMeasuredHeight();
        //重新适配展示的高度  当前高度+headView的高度
        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) + maxY, MeasureSpec.EXACTLY));
    }

    private boolean isIntercepted = false;
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (null == mVelocityTracker) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }

                scrollY = event.getY();
                lastScrollY = scrollY;

                //****************************************************************************************************
                //*****这是的拦截是为了适配headView是ViewPager的时候,并且ViewPager的View还是ListView这种可滚动的View*****
                //*****如果headView不是ViewPager或者ViewPager里的View不是可滚动的View,可以去掉这个拦截判断***************
                if(getChildAt(0) instanceof ViewPager) {
                    //这两个判断是为了适配顶部是ViewPager的情况,如果没有ViewPager可以直接执行结构体就行
                    if (isVerticalInView(event, getChildAt(0))) {
                        isIntercepted = true;
                        break;
                    }
                }
                isIntercepted = false;
                //****************************************************************************************************

                break;
            case MotionEvent.ACTION_MOVE:
                scrollY = event.getY();
                //判断当前Y的值是否等于或者大于HeadView的高度,并且子滚动ScrollView没有滑动到最顶部的时候:屏蔽当前滚动事件,事件交给ScrollView处理
                if((mCurY==mHeadHeight||mCurY>mHeadHeight)&&!isTop()){
                    break;
                }

                //****************************************************************************************************
                //*****这是的拦截是为了适配headView是ViewPager的时候,并且ViewPager的View还是ListView这种可滚动的View*****
                //*****如果headView不是ViewPager或者ViewPager里的View不是可滚动的View,可以去掉这个拦截判断***************
                if(isIntercepted){
                    break;
                }
                //****************************************************************************************************

                int scrolledY = (int) (lastScrollY - scrollY);
                if (scrolledY < 0 && getScrollY() + scrolledY < 0) {                      //向下滚动
                    scrolledY = -getScrollY();
                }
                if (scrolledY > 0 && getScrollY() + scrolledY > mScrollRange) {          //向上滚动
                    scrolledY = mScrollRange - getScrollY();
                }
                if (mScrollRange > 0 ) {                                                  //可滚动距离必须大于0
                    scrollBy(0, scrolledY);
                    invalidate();
                }
                lastScrollY = scrollY;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mVelocityTracker != null && mScrollRange > 0) {
                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
                    mScroller.fling(getScrollX()
                            , getScrollY()                                             //松开滑动垂直开始坐标
                            , (int) mVelocityTracker.getXVelocity(0)
                            , -(int) mVelocityTracker.getYVelocity(0)                  //垂直方向滑动加速度,0表示第一个按下手指
                            , 0                                                        //松开滑动水平最小坐标
                            , 0                                                        //松开滑动垂直最小坐标
                            , 0                                                        //松开滑动水平最大坐标
                            , mScrollRange);                                           //松开滑动垂直最大坐标(这里就是最大滑动范围)
                    invalidate();                                                      //这必须调用刷新否则看不到效果
                }
                releaseVelocityTracker();
                break;
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    public void computeScroll() {
        //判断当前Y的值是否等于或者大于HeadView的高度,也就是HeadView隐藏的时候,不执行scrollTo当前View不可滑动,交给子View(ScrollView滑动)
        if((mCurY==mHeadHeight||mCurY>mHeadHeight)){
            return;
        }
        //这里不管是不是滑动到顶部,都需要执行scrollTo,否则headView会直接弹出来效果不对
        if (mScroller.computeScrollOffset()) {

            //****************************************************************************************************
            //*****这是的拦截是为了适配headView是ViewPager的时候,并且ViewPager的View还是ListView这种可滚动的View*****
            //*****如果headView不是ViewPager或者ViewPager里的View不是可滚动的View,可以去掉这个拦截判断***************
            if(isIntercepted){
                return;
            }
            //****************************************************************************************************

            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate(); //这必须调用刷新否则看不到效果
        }
    }

    @Override
    public void scrollBy(int x, int y) {
        int scrollY = getScrollY();
        int toY = scrollY + y;
        //maxY是HeadView的高度,用来实现sticky固定
        if (toY >= maxY) {
            toY = maxY;
        } else if (toY <= minY) {
            toY = minY;
        }
        y = toY - scrollY;
        super.scrollBy(x, y);
    }

    /**
     * 整个控件的高度就是展示在屏幕上的高度加上header控件的高度,所以最多只能向下滑动header的高度
     *
     * @param x //因为不能左右滑,所以始终未0
     * @param y //纵向滑动的y值,判断sticky固定的位置
     */
    @Override
    public void scrollTo(int x, int y) {
        //maxY是HeadView的高度,用来实现sticky固定
        if (y >= maxY) {
            y = maxY;
        } else if (y <= minY) {
            y = minY;
        }
        mCurY = y;
        super.scrollTo(x, y);
    }

    public boolean isTop(){
        View scrollView = getChildAt(2);
        if(scrollView instanceof ScrollView){
            return isScrollViewTop((ScrollView) scrollView);
        }
        if(scrollView instanceof AdapterView){
            return isAdapterViewTop((AdapterView) scrollView);
        }
        return false;
    }

    private boolean isScrollViewTop(ScrollView scrollView) {
        if (scrollView != null) {
            int scrollViewY = scrollView.getScrollY();
            Log.d(this.getClass().getSimpleName(),"scrollViewY ===>" + scrollViewY);
            return scrollViewY <= 0;
        } else {
            Log.d(this.getClass().getSimpleName(),"scrollView is null");
        }
        return false;
    }

    private boolean isAdapterViewTop(AdapterView adapterView) {
        if (adapterView != null) {
            int firstVisiblePosition = adapterView.getFirstVisiblePosition();
            View childAt = adapterView.getChildAt(0);
            return childAt == null || (firstVisiblePosition == 0 && childAt.getTop() == 0);
        }
        return false;
    }

    /**
     * mVelocityTracker回收
     */
    private void releaseVelocityTracker() {
        if (null != mVelocityTracker) {
            mVelocityTracker.clear();
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    private boolean isVerticalInView(MotionEvent ev, View view){
        if(view == null){
            return false;
        }

        //Y轴移动的值 (正值代表向上移动,负值代表向下移动)
        int scrollY = getScrollY();

        int listTop = view.getTop()-scrollY ;
        int listBtm = view.getBottom() -scrollY;
        int listLeft = view.getLeft();
        int listR = view.getRight();
        int y = (int) ev.getY();
        int x = (int) ev.getX();

        return x > listLeft && x < listR && y > listTop && y < listBtm;
    }

    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {}

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {}

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return false;
    }
}

三、布局xml使用

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.mot.android_view.views.sticky.sticky_layout_mot.StickyLinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <!--  适配ViewPager的展示,并且ViewPager里的View是ListView  -->
<!--        <com.mot.android_view.views.dispatch.views.BaseViewPager-->
<!--            android:id="@+id/baseViewPager"-->
<!--            android:layout_width="match_parent"-->
<!--            android:layout_height="300dp"/>-->

        <TextView
            android:id="@+id/headView"
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:gravity="center"
            android:text="TextView展示HeadView\n\n可以是ViewPager\nViewPager的子View可以是ListView" />

        <TextView
            android:id="@+id/stickyView"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="@color/color_red"
            android:gravity="center"
            android:text="StickyView用作展示,也可以换成其他的View"
            android:textColor="@color/color_white" />

        <!--   这里可以是ScrollView、ListView、RecyclerView等可滚动的Viwe     -->
        <ListView
            android:id="@+id/motListView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    </com.mot.android_view.views.sticky.sticky_layout_mot.StickyLinearLayout>

</androidx.appcompat.widget.LinearLayoutCompat>

四、activity测试stickyView

public class StickyLinearLayoutActivity extends AppCompatActivity {
    private ListView motListView;
    private BaseViewPager baseViewPager;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_android_view_sticky_linearlayout_mot_page);
        motListView = (ListView) findViewById(R.id.motListView);
//        baseViewPager = (BaseViewPager)findViewById(R.id.baseViewPager);
    }

    @Override
    protected void onResume() {
        super.onResume();
//        initViewPager();
        initMoreListView();
    }

    private void initMoreListView(){
        List<ListBean> listBeans = new ArrayList<>();
        ListBean listBean = new ListBean();
        listBean.setName("漠天");
        listBean.setPhone("15201498667");
        listBeans.add(listBean);
        ListBean listBean1 = new ListBean();
        listBean1.setName("黑天河");
        listBean1.setPhone("13831048122");
        listBeans.add(listBean1);
        ListBean listBean2 = new ListBean();
        listBean2.setName("堕落风");
        listBean2.setPhone("12323248844");
        listBeans.add(listBean2);
        ListBean listBean3 = new ListBean();
        listBean3.setName("湖天地");
        listBean3.setPhone("999999999999");
        listBeans.add(listBean3);
        listBeans.add(listBean2);
        listBeans.add(listBean1);
        listBeans.add(listBean3);
        listBeans.add(listBean2);
        listBeans.add(listBean3);
        listBeans.add(listBean2);
        listBeans.add(listBean1);
        listBeans.add(listBean3);
        listBeans.add(listBean2);
        listBeans.add(listBean3);
        listBeans.add(listBean2);
        listBeans.add(listBean1);
        listBeans.add(listBean3);
        listBeans.add(listBean2);
        ConflictListViewAdapter conflictListViewAdapter = new ConflictListViewAdapter();
        motListView.setAdapter(conflictListViewAdapter);
        conflictListViewAdapter.addListBean(listBeans);
    }

    private void initViewPager(){

        LayoutInflater layoutInflater = LayoutInflater.from(this);
        View firstPageView = layoutInflater.inflate(R.layout.viewpager_first_layout,null);
        View secondPageView = layoutInflater.inflate(R.layout.viewpager_second_layout,null);
        View thirdPageView = layoutInflater.inflate(R.layout.viewpager_third_layout,null);

        BaseListView firstBaseListView =firstPageView.findViewById(R.id.baseFirstListView);
        BaseListView secondBaseListView =secondPageView.findViewById(R.id.baseSecondListView);
        BaseListView thirdBaseListView =thirdPageView.findViewById(R.id.baseThirdListView);

        ArrayAdapter<String> testArrayAdapter = new ArrayAdapter<String>(this,R.layout.list_item_layout,R.id.showText,getTempListData());

        firstBaseListView.setAdapter(testArrayAdapter);
        secondBaseListView.setAdapter(testArrayAdapter);
        thirdBaseListView.setAdapter(testArrayAdapter);

        List<View> views = new ArrayList<>();
        views.add(firstPageView);
        views.add(secondPageView);
        views.add(thirdPageView);

        baseViewPager.setAdapter(new ListPagerAdapter(views));
    }

    private List<String> getTempListData(){
        List<String> resourceList = new ArrayList<>();
        resourceList.add("0000000000000000000000000000");
        resourceList.add("111111111111111111111111");
        resourceList.add("2222222222222222222222222");
        resourceList.add("33333333333333333333333333");
        resourceList.add("4444444444444444444444444");
        resourceList.add("5555555555555555555555555");
        resourceList.add("6666666666666666666666666666");
        resourceList.add("777777777777777777777777777777");
        resourceList.add("88888888888888888888888888888");
        resourceList.add("9999999999999999999999999999");
        resourceList.add("*****************************");
        resourceList.add("");
        return resourceList;
    }

    /**
     * viewpager 适配器
     */
    private static class ListPagerAdapter extends PagerAdapter {

        private List<View> mListViews=new ArrayList<>();

        public ListPagerAdapter(List<View> listViews) {
            mListViews = listViews;
        }

        @Override
        public int getCount() {
            return mListViews.size();
        }

        @Override
        public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
            return view==object;
        }

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            container.addView(mListViews.get(position));
            return mListViews.get(position);
        }

        @Override
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            container.removeView(mListViews.get(position));
        }
    }

}

关键点:

1、ViewGroup的事件分发很重要

2、思路清晰很重要

3、scrollBy和scrollTo的区别,实现sticky效果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
粘性控件,其任意一个子控件都可滑动停留,无论是View,还是ViewGroup;用该控件可以轻松实现支付宝"全部应用"界面。演示图  Note:图1为设置属性wkp_canScrollToEndViewTop=true,图2没有;图3为设置滑动改变监听。Gradle集成dependencies{       compile 'com.wkp:StickLayout:1.0.6'       //Android Studio3.0 可用以下方式       //implementation 'com.wkp:StickLayout:1.0.6' } //如不愿意等待,请加上我的maven仓库地址 maven { url "https://dl.bintray.com/wkp/maven" }Note:可能存在Jcenter还在审核阶段,这时会集成失败!注意SDK版本targetSdkVersion >= 26.使用详解属性讲解<!--是否粘性停留(用于直接子控件)-->         <attr name="wkp_stick" format="boolean"/>         <!--是否开启滑动到最后一个控件的顶部,默认不开启(用于控件本身),注意最后一个子控件如果为条目控件时,如ListView,请不要开启-->         <attr name="wkp_canScrollToEndViewTop" format="boolean"/>Note:每个属性都有对应的java设置代码!布局<?xml version="1.0" encoding="utf-8"?> <LinearLayout     xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:orientation="vertical">     <TextView         android:clickable="true"         android:onClick="addView"         android:gravity="center"         android:padding="5dp"         android:text="添加条目"         android:layout_width="match_parent"         android:layout_height="wrap_content"/>         <!--app:wkp_canScrollToEndViewTop="true"-->     <com.wkp.sticklayout_lib.widget.StickLayout         android:id="@ id/sl"         android:layout_width="match_parent"         android:layout_height="wrap_content">         <TextView             android: id/tv1"             android:text="第1行"             android:gravity="center"             android:layout_width="match_parent"             android:layout_height="200dp"/>         <LinearLayout             app:wkp_stick="true"             android:orientation="horizontal"             android:layout_width="match_parent"             android:layout_height="40dp">             <TextView                 android:onClick="scrollTo"                 android:background="@android:color/holo_blue_light"                 android:text="NUM2"                 android:gravity="center"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>             <TextView                 android:onClick="scrollTo3"                 android:background="@android:color/holo_green_light"                 android:text="NUM3"                 android:gravity="center"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>             <TextView                 android:onClick="scrollTo4"                 android:background="@android:color/holo_red_light"                 android:text="NUM4"                 android:gravity="center"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>             <TextView                 android:onClick="scrollTo7"                 android:background="@android:color/holo_orange_light"                 android:text="NUM7"                 android:gravity="center"                 android:layout_weight="1"                 android:layout_width="0dp"                 android:layout_height="match_parent"/>         </LinearLayout>         <TextView             android:id="@ id/tv2"             android:text="第2行"             android:background="@android:color/holo_blue_light"             android:gravity="center"             android:layout_width="match_parent"             android:layout_height="200dp"/>         <TextView             android:id="@ id/tv3"             app:wkp_stick="true"             android:text="第3行"             android:background="@android:color/holo_green_light"             android:gravity="center"             android:layout_width="match_parent"             android:layout_height="200dp"/>         <TextView             android:background="@android:color/holo_red_light"             android:id="@ id/tv4"             android:text="第4行"             android:gravity="center"             android:layout_width="match_parent"             android:layout_height="200dp"/>         <TextView             android:id="@ id/tv5"             android:text="第5行"             android:gravity="center"             android:layout_width="match_parent"             android:layout_height="200dp"/>         <TextView             android:id="@ id/tv6"             android:text="第6行"             android:gravity="center"             android:layout_width="match_parent"             android:layout_height="200dp"/>         <TextView             android:background="@android:color/holo_orange_light"             android:id="@ id/tv7"             android:text="第7行"             android:gravity="center"             android:layout_width="match_parent"             android:layout_height="200dp"/>     </com.wkp.sticklayout_lib.widget.StickLayout> </LinearLayout>Note:ScrollView嵌套StickLayout时事件被拦截,无效果!StickLayout嵌套如ListView的条目控件时会只显示第一行,注意解决!代码示例public class MainActivity extends AppCompatActivity {     private StickLayout mSl;     private TextView mTv2;     private View mTv3;     private View mTv7;     private View mTv4;     private int currentPosition = -1;     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         mSl = findViewById(R.id.sl);         mTv2 = findViewById(R.id.tv2);         mTv3 = findViewById(R.id.tv3);         mTv4 = findViewById(R.id.tv4);         mTv7 = findViewById(R.id.tv7); //        mSl.setStickView(findViewById(R.id.tv2)); //设置粘性控件 //        mSl.setStickView(findViewById(R.id.tv3)); //        mSl.canScrollToEndViewTop(true);      //设置是否开启最后控件滑动到顶部         //设置滑动改变监听(一滑动就会有回调)         mSl.setOnScrollChangeListener(new StickLayout.OnScrollChangeListener() {             @Override             public void onScrollChange(StickLayout v, View currentView, int position, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {                 //直到当前控件改变在做事情                 if (currentPosition != position) {                     Toast.makeText(v.getContext(), ((TextView) currentView).getText().toString(), Toast.LENGTH_SHORT).show();                     currentPosition = position;                 }             }         });     }     public void addView(View view) {         TextView textView = new TextView(view.getContext());         textView.setGravity(Gravity.CENTER);         textView.setPadding(10, 10, 10, 10);         textView.setText("新条目");         mSl.addView(textView, 0);     }     public void scrollTo2(View view) {         //滑动到指定子控件         mSl.scrollToView(mTv2);     }     public void scrollTo3(View view) {         mSl.scrollToView(mTv3);     }     public void scrollTo4(View view) {         mSl.scrollToView(mTv4);     }     public void scrollTo7(View view) {         mSl.scrollToView(mTv7);     } }Note:还有其他API请根据需要自行参考!寄语控件支持直接代码创建,还有更多API请观看StickLayout.java内的注释说明。欢迎大家使用,感觉好用请给个Star鼓励一下,谢谢!大家如果有更好的意见或建议以及好的灵感,请邮箱作者,谢谢!QQ邮箱:1535514884@qq.com163邮箱:15889686524@163.comGmail邮箱:wkp15889686524@gmail.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漠天515

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值