一个可滑动的标签布局TagLayout~(支持滑动,gravity,间距等配置)2

上一篇:https://blog.csdn.net/CSDN_JIUWANG/article/details/119187166

额。。。对于未完成的预期版本,总会想着尝试去做一下试试看能不能完成,所以又又又又又在前面的版本上改了。。。。但其实这里只是做了一些优化和我前面一直说的不支持排列方向的事。。。后面可能还会改。。至少现在的排列方向上逻辑还不够严谨(但是是没问题的)

效果图:

gif好大,放不下 ,演示不了了,,,,

啊,传了个演示视频

代码:

package com.example.admin.view.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.IntDef;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Scroller;

import com.example.admin.view.R;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static android.view.View.MeasureSpec.AT_MOST;
import static com.example.admin.view.view.ScrollableExtendedOptimizedSimpleTagAdapterLayout2.SelectMode.SELECT_MODE_MAX;
import static com.example.admin.view.view.ScrollableExtendedOptimizedSimpleTagAdapterLayout2.SelectMode.SELECT_MODE_MIN;
import static com.example.admin.view.view.ScrollableExtendedOptimizedSimpleTagAdapterLayout2.SelectMode.SELECT_MODE_MULTIPLE;
import static com.example.admin.view.view.ScrollableExtendedOptimizedSimpleTagAdapterLayout2.SelectMode.SELECT_MODE_RANGE;
import static com.example.admin.view.view.ScrollableExtendedOptimizedSimpleTagAdapterLayout2.SelectMode.SELECT_MODE_SINGLE;

/**
 * @ProjectName: gate_app
 * @Package: com.greentravel.smart.view
 * @ClassName: TabLayout
 * @Description: 引用适配器管理数据的标签布局
 * one.目前只适用于标签一致(同一个类型的标签布局且高度一致)
 * 2.目前仅支持横向布局
 * 3.添加删除数据时,已做优化(起始index及后续全部发生变化,index前不会改变)
 * one.重新添加子view
 * b.绑定监听
 * 绑定监听这里对全部标签统一做了连续点击控制处理,即点击标签A无法在间隔时间内响应标签B的点击事件。
 * c.测量子view
 * d.布局子view
 * 发现bug:单次添加执行数据操作是没有问题的,但是在短时间内连续多次添加数据时,measure和layout并不会每次都执行,
 * 而是只执行最后一次的调度(measure一次被调用若干次,layout被调用一次,貌似系统做过处理)所以,测量和布局没办法优化了。
 * 当然,如果你确定你的数据操作没有那么频繁,依然可以做此优化
 * 4.不考虑标签可见性和高度不一致问题
 * 5.禁止直接添加子view(手动布局重绘)
 * 6.支持gravity
 * 7.支持选择模式
 * 8.支持orientation
 * 9.支持滑动
 * one.滑动控制 滑动边界缓存控制
 * b.边界滑动回弹scroller.startScroll+override computeScroll()
 * c.滑翔scroller.fling + VelocityTracker,使滑动不生硬
 * d.未作多指操作event.findPointerIndex + event.getY(pointerIndex)
 * @Author: wangwei
 * @CreateDate: 2021/7/8 14:15
 * @UpdateUser:
 * @UpdateDate: 2021/7/8 14:15
 * @UpdateRemark:
 * @Version: one.0
 */
public class ScrollableExtendedOptimizedSimpleTagAdapterLayout2 extends ViewGroup {

    private static final String TAG = " TagAdapterLayout ";

    private int start = 0;//数据添加删除时的起始标记位
    private int wContentSize;//内容总宽度(含padding)
    private int hContentSize;//内容总高度(含padding)

    private int doubleClickInterval = 200;//标签有效双击时间间隔
    private int columnRowSpace = 10;//行/列间距
    private int itemSpace = 10;//排列方向的标签间距
    private int gravity = Gravity.RIGHT;

    public class SelectMode {
        public static final int SELECT_MODE_SINGLE = 0x01;
        public static final int SELECT_MODE_MULTIPLE = 0x02;
        public static final int SELECT_MODE_MAX = 0x03;
        public static final int SELECT_MODE_MIN = 0x04;
        public static final int SELECT_MODE_RANGE = 0x05;
    }

    private List<View> selectedViews;

    private int selectMode = SELECT_MODE_MULTIPLE;//选择模式
    private int maxSelect = 2;
    private int minSelect = 1;

    @IntDef({HORIZONTAL, VERTICAL})
    @Retention(RetentionPolicy.SOURCE)
    public @interface OrientationMode {
    }

    public static final int HORIZONTAL = 0;
    public static final int VERTICAL = 1;

    private int orientation = HORIZONTAL;

    /**
     * 后来发现滑动缓冲回弹系统有支持23333~ api>9
     * OverScroller.overScrollBy
     * onOverScrolled():过渡滑动监听,当过渡时调用下面函数回弹
     * OverScroll.springBack():过渡回弹
     */
    private int bufferDistance = 200;//边界滑出最大缓冲距离
    private int flingBufferDistance = bufferDistance / 2;//fling操作滑出边界最大缓冲距离,距离越大,等待回弹时间越长
    private boolean supportBufferDistance = true;//是否支持缓冲距离
    private boolean scrollable = false;//是否支持滑动
    private boolean isAllowOverFling = true;//是否支持过渡滑翔
    private boolean isAllowOverScroll = true;//是否支持过渡滑动

    private Scroller scroller;
    private VelocityTracker velocityTracker;
    private boolean needScroll;

    private TagAdapter adapter;
    private OnTagClickListener onTagClickListener;
    private OnTagLongClickListener onTagLongClickListener;
    private OnSelectChangedListener onSelectChangedListener;


    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2(Context context) {
        this(context, null);
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
    }

    private void initFromAttributes(
            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        final TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.TagLayout, defStyleAttr, defStyleRes);
        itemSpace = a.getDimensionPixelOffset(R.styleable.TagLayout_itemSpace, itemSpace);
        columnRowSpace = a.getDimensionPixelOffset(R.styleable.TagLayout_lineSpace, columnRowSpace);
        gravity = a.getInt(R.styleable.TagLayout_gravity, gravity);
        selectMode = a.getInt(R.styleable.TagLayout_select_mode, selectMode);
        maxSelect = a.getInt(R.styleable.TagLayout_max_select, maxSelect);
        minSelect = a.getInt(R.styleable.TagLayout_min_select, minSelect);
        orientation = a.getInt(R.styleable.TagLayout_orientation, orientation);
        bufferDistance = a.getInt(R.styleable.TagLayout_bufferDistance, bufferDistance);
        scrollable = a.getBoolean(R.styleable.TagLayout_scrollable, scrollable);
        log(TAG + "gravity = " + gravity);
        a.recycle();
        init();
    }

    /*private String[] strings = new String[]{"这是一个测试文本",
            "Me Too!", "巧了,我也是一个测试文本,而且比你俩长", "那我岂不是更要长,看我有多长,是不是你们当中最长的啊,我是你们当中最长的测试文本了", "好羡慕啊", "哎、", "噢噢噢噢哦哦哦",
            "这是一个测试文本",
            "Me Too!", "巧了,我也是一个测试文本,而且比你俩长", "那我岂不是更要长,看我有多长,是不是你们当中最长的啊,我是你们当中最长的测试文本了", "好羡慕啊", "哎、", "噢噢噢噢哦哦哦"};*/
  

    private void init() {
        selectedViews = new ArrayList<>();
        if (selectMode == SELECT_MODE_RANGE && maxSelect <= minSelect) {
            maxSelect = minSelect + 1;
        }
        scroller = new Scroller(getContext());
        velocityTracker = VelocityTracker.obtain();
    }

    public void setAdapter(TagAdapter adapter) {
        this.adapter = adapter;
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2 setLineSpacePx(int lineSpace) {
        if (this.columnRowSpace != lineSpace) {
            this.columnRowSpace = lineSpace;
            requestLayout();
        }
        return this;
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2 setLineSpaceDp(int lineSpace) {
        if (this.columnRowSpace != dip2px(lineSpace)) {
            this.columnRowSpace = dip2px(lineSpace);
            requestLayout();
        }
        return this;
    }

    public int getColumnRowSpace() {
        return columnRowSpace;
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2 setItemSpacePx(int itemSpace) {
        if (this.itemSpace != itemSpace) {
            this.itemSpace = itemSpace;
            requestLayout();
        }
        return this;
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2 setItemSpaceDp(int itemSpace) {
        if (this.itemSpace != dip2px(itemSpace)) {
            this.itemSpace = dip2px(itemSpace);
            requestLayout();
        }
        return this;
    }

    public int getItemSpace() {
        return itemSpace;
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2 setDoubleClickInterval(int interval) {
        doubleClickInterval = interval;
        return this;
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2 setSelectMode(int selectMode) {
        this.selectMode = selectMode;
        if (this.selectMode == SELECT_MODE_RANGE && maxSelect <= minSelect) {
            maxSelect = minSelect + 1;
        }
        return this;
    }

    public int getSelectMode() {
        return selectMode;
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2 setMaxSelect(int maxSelect) {
        this.maxSelect = maxSelect;
        if (selectMode == SELECT_MODE_RANGE && this.maxSelect <= minSelect) {
            minSelect = maxSelect - 1;
        }
        return this;
    }

    public int getMaxSelect() {
        return maxSelect;
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2 setMinSelect(int minSelect) {
        this.minSelect = minSelect;
        if (selectMode == SELECT_MODE_RANGE && this.maxSelect <= minSelect) {
            this.maxSelect = minSelect + 1;
        }
        return this;
    }

    public int getMinSelect() {
        return minSelect;
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2 setGravity(int gravity) {
        if (this.gravity != gravity) {
            this.gravity = gravity;
            requestLayout();
        }
        return this;
    }

    public int getGravity() {
        return gravity;
    }

    public ScrollableExtendedOptimizedSimpleTagAdapterLayout2 setOrientation(@OrientationMode int orientation) {
        if (this.orientation !
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是可滑动的好友列表界面的实现代码。 首先,我们需要创建一个包含 ViewPager 和 TabLayout布局文件,例如 `activity_main.xml`: ```xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimary" android:minHeight="?attr/actionBarSize" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:tabGravity="fill" app:tabIndicatorColor="@color/colorAccent" app:tabMode="scrollable" /> <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/tab_layout" /> </RelativeLayout> ``` 接下来,我们需要创建两个 Fragment 类来展示好友列表,例如 `RecentFriendsFragment` 和 `AllFriendsFragment`。 `RecentFriendsFragment` 类的代码如下: ```java public class RecentFriendsFragment extends Fragment { public RecentFriendsFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_recent_friends, container, false); } } ``` `AllFriendsFragment` 类的代码和 `RecentFriendsFragment` 类的代码类似,这里不再赘述。 接下来,我们需要创建一个 Adapter 类来将两个 Fragment 添加到 ViewPager 中,例如 `FriendsPagerAdapter` 类: ```java public class FriendsPagerAdapter extends FragmentPagerAdapter { private String[] tabTitles = new String[]{"Recent", "All"}; public FriendsPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { switch (position) { case 0: return new RecentFriendsFragment(); case 1: return new AllFriendsFragment(); default: return null; } } @Override public int getCount() { return tabTitles.length; } @Override public CharSequence getPageTitle(int position) { return tabTitles[position]; } } ``` 最后,在 `MainActivity` 中设置 ViewPager 和 TabLayout,例如: ```java public class MainActivity extends AppCompatActivity { private ViewPager viewPager; private TabLayout tabLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager = findViewById(R.id.view_pager); tabLayout = findViewById(R.id.tab_layout); FriendsPagerAdapter adapter = new FriendsPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(adapter); tabLayout.setupWithViewPager(viewPager); } } ``` 好了,以上就是使用 ViewPager 和 Fragment 做一个简单版的好友列表界面的全部代码。如果你需要详细的 import 和配置,可以参考下面的完整代码: `activity_main.xml`: ```xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimary" android:minHeight="?attr/actionBarSize" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:tabGravity="fill" app:tabIndicatorColor="@color/colorAccent" app:tabMode="scrollable" /> <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/tab_layout" /> </RelativeLayout> ``` `fragment_recent_friends.xml` 和 `fragment_all_friends.xml`: ```xml <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:textSize="24sp" /> ``` `RecentFriendsFragment` 和 `AllFriendsFragment`: ```java public class RecentFriendsFragment extends Fragment { public RecentFriendsFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_recent_friends, container, false); } } public class AllFriendsFragment extends Fragment { public AllFriendsFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_all_friends, container, false); } } ``` `FriendsPagerAdapter`: ```java public class FriendsPagerAdapter extends FragmentPagerAdapter { private String[] tabTitles = new String[]{"Recent", "All"}; public FriendsPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { switch (position) { case 0: return new RecentFriendsFragment(); case 1: return new AllFriendsFragment(); default: return null; } } @Override public int getCount() { return tabTitles.length; } @Override public CharSequence getPageTitle(int position) { return tabTitles[position]; } } ``` `MainActivity`: ```java public class MainActivity extends AppCompatActivity { private ViewPager viewPager; private TabLayout tabLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager = findViewById(R.id.view_pager); tabLayout = findViewById(R.id.tab_layout); FriendsPagerAdapter adapter = new FriendsPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(adapter); tabLayout.setupWithViewPager(viewPager); } } ``` 注意需要在 `build.gradle` 中添加以下依赖: ```groovy implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support:design:28.0.0' ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值