0%

调整tab的分组和顺序仿今日头条


前段时间太忙了,终于可以抓紧周末的时间更一更blog,公司的项目有个这样的需求,一个界面的顶部有个tab分页栏,tab分页栏中的tab个数以及顺序都是可变的。在分页栏的最右侧有个按钮,点击按钮可进入分页tab的管理界面,在管理界面中,分为可见与不可见两组,可见组才能在界面顶部的tab分页栏中显示。可见组可拖拽排序,点击编辑按钮,在编辑状态下,可见组的item右上角出现按钮,点击后可调整到不可见组,非编辑状态下,点击item可跳转至该分页的界面。不可见组的item点击就会调整到可见组。效果类似今日头条的自定义频道功能。

整理大致思路如下,关于tab分页栏可用google在android 6.0上引入的控件tabLayout,需support v7包的支持,具体使用方法可以查看官网,这应该是最简单的实现方式了,不过这里有个坑,就是绑定了viewpager后不显示tab中的内容,查看源码可知是被清除了,需要重新绑定一次tab的view。有更高的自定义需求可参考这个[开源项目][1]。管理界面的拖拽可通过重写recyclerview的拖拽监听来实现,这里需要实现item的移动动画。


先放一张效果图:
效果gif

主界面的tab栏用TabLayout实现

主要用到一下几个属性,更多属性用法可参考官方文档:

  • app:tabIndicatorColor :指示条的颜色

  • app:tabIndicatorHeight :指示条的高度

  • app:tabSelectedTextColor : tab被选中时的字体颜色

  • app:tabTextColor : tab未被选中时的字体颜色

  • app:tabMode=”scrollable” : 默认是fixed:固定的,标签很多时候会被挤压,不能滑动。

  • app:tabBackground=: tab的背景

下面是主界面的布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/indicator_yellow"
android:layout_weight="1"
app:tabBackground="@color/indicator_yellow"
app:tabIndicatorColor="@color/select_red_line"
app:tabIndicatorHeight="2dp"
app:tabSelectedTextColor="@color/indicator_select_black"
app:tabTextColor="@color/indicator_normal_black"
app:tabMode="scrollable"
/>

<ImageView
android:id="@+id/icon_category"
android:layout_width="40dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@color/indicator_yellow"
android:paddingLeft="5dp"
android:scaleType="center"
android:src="@drawable/sl_main_title_add" />
</LinearLayout>

<!--内容布局-->
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />


</LinearLayout>

TabLayout的初始化

  1. 定义viewpager的adapter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    public class ExamplePagerAdapter extends PagerAdapter {
    private List<String> mDataList;

    public ExamplePagerAdapter(List<String> dataList) {
    mDataList = dataList;
    }

    @Override
    public int getCount() {
    return mDataList == null ? 0 : mDataList.size();
    }

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
    TextView textView = new TextView(container.getContext());
    textView.setText(mDataList.get(position));
    textView.setGravity(Gravity.CENTER);
    textView.setTextColor(Color.BLACK);
    textView.setTextSize(24);
    container.addView(textView);
    return textView;
    }

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

    @Override
    public int getItemPosition(Object object) {
    TextView textView = (TextView) object;
    String text = textView.getText().toString();
    int index = mDataList.indexOf(text);
    if (index >= 0) {
    return index;
    }
    return POSITION_NONE;
    }

    @Override
    public CharSequence getPageTitle(int position) {
    return mDataList.get(position);
    }
    }
  2. 初始化viewpager和TabLayout

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    tabLayout.setupWithViewPager(viewPager);
    setTabTitle(true);
    viewPager.setAdapter(mExamplePagerAdapter);

    private void setTabTitle(boolean addTab) {
    mDataList.clear();
    Iterator<MainTitleDTO> it = items.iterator();
    while (it.hasNext()) {
    MainTitleDTO mainTitleDTO = it.next();
    mDataList.add(mainTitleDTO.getTitle());
    if(addTab){
    tabLayout.addTab(tabLayout.newTab().setText(mainTitleDTO.getTitle()));
    }
    }
    }

    注意这里容易入坑,调用tabLayout.setupWithViewPager(viewPager);会清除tab。
    看下源码我们就可以得知真相:第一次用TabLayout的时候也在这里卡住了,Are You Kidding Me?真的好坑~
    Android源码中的坑
    所以有两个解决办法,一是在这个调用后去初始化tabLayout中的tab,二是在调用tabLayout.setupWithViewPager(viewPager);后再次设置tabLayout中的tab,代码如下:

    1
    2
    3
    for(int i=0;i<title.size();i++){
    tabLayout.getTabAt(i).setText(title.get(i));
    }

    然后是主页tab栏右边进入管理界面的点击监听

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @OnClick({R.id.icon_category})
    public void onEventClick(View view) {
    switch (view.getId()) {
    case R.id.icon_category:
    Intent intent = new Intent(this, TabManageActivity.class);
    intent.putExtra(MY_CHANNEL, items);
    intent.putExtra(OTHER_CHANNEL, otherItems);
    startActivityForResult(intent, REQUEST_CODE);
    break;
    }
    }

    tab管理界面的实现

    这里主要是用recyclerview实现,布局很简单,xml文件如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_channel"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <ImageView
    android:id="@+id/icon_collapse"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="right"
    android:layout_marginTop="3.0dip"
    android:padding="10.0dip"
    android:scaleType="center"
    android:src="@drawable/select_channel_category_edit_close" />


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

    <android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp"/>

    </FrameLayout>
    </LinearLayout>

    自定义recyclerview的adapter

    这里是实现调整tab分组和顺序的核心,封装了一个库
    封装好的lib库结构如下:
    libtabmanage结构

  3. 自定义接口选中与拖拽后监听

    1
    2
    3
    4
    5
    6
    public interface OnItemDragListener {
    //Item选中监听
    void onItemSelected();
    //Item拖拽结束/滑动结束后监听
    void onItemFinish();
    }
  4. 自定义接口移动后监听

    1
    2
    3
    public interface OnItemMoveListener {
    void onItemMove(int fromPosition, int toPosition);
    }
  5. 重写recyclerview的拖拽回调

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    public class ItemDragHelperCallback extends ItemTouchHelper.Callback {
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    int dragFlags;
    RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
    if (manager instanceof GridLayoutManager || manager instanceof StaggeredGridLayoutManager) {
    dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
    } else {
    dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
    }
    // 如果想支持滑动(删除)操作, swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END
    int swipeFlags = 0;
    return ItemTouchHelper.Callback.makeMovementFlags(dragFlags, swipeFlags);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
    // 当前type与目标type不同,不同Type之间不可移动
    if (viewHolder.getItemViewType() != target.getItemViewType()) {
    return false;
    }

    //移动后的监听调用
    if (recyclerView.getAdapter() instanceof OnItemMoveListener) {
    OnItemMoveListener listener = ((OnItemMoveListener) recyclerView.getAdapter());
    listener.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
    }
    return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    }

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
    // 不在闲置状态,调用拖拽监听
    if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
    if (viewHolder instanceof OnItemDragListener) {
    OnItemDragListener itemViewHolder = (OnItemDragListener) viewHolder;
    itemViewHolder.onItemSelected();
    }
    }
    super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    //拖拽后的监听
    if (viewHolder instanceof OnItemDragListener) {
    OnItemDragListener itemViewHolder = (OnItemDragListener) viewHolder;
    itemViewHolder.onItemFinish();
    }
    super.clearView(recyclerView, viewHolder);
    }

    @Override
    public boolean isLongPressDragEnabled() {
    // 不支持长按拖拽功能
    return false;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
    // 不支持滑动功能
    return false;
    }

    由于非可见组的item点击后要自动调整到可见组,可见组的item通过长按或者点击编辑按钮,切换到编辑状态时,点击右上角的叉,要自动调整到非可见组,所以这里需要实现两个动画,item从可见组当前位置移动到非可见组的首位,item从非可见组的当前位置移动到可见组的末尾。

  6. item动画工具类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    public class RecyclerviewAnimator {

    public static Handler delayHandler = new Handler();//延时handler
    private static final long ANIM_TIME = 360L;//动画时间
    //我的分页移动到其他分页
    public static void myToOther(int position, RecyclerView parent, RecyclerView.Adapter adapter,RecyclerView.ViewHolder myHolder, List myItems,List otherItems){
    if(position == 1){//过滤第一个
    return;
    }
    RecyclerView recyclerView = parent;
    //目标view是其他分页的第一个
    View targetView = recyclerView.getLayoutManager().findViewByPosition(myItems.size() + BaseTabListAdapter.COUNT_PRE_OTHER_HEADER);
    //当前view
    View currentView = recyclerView.getLayoutManager().findViewByPosition(position);
    // 如果targetView不在屏幕内,则indexOfChild为-1,此时不需要添加动画,因为此时notifyItemMoved自带一个向目标移动的动画
    // 如果在屏幕内,则添加一个位移动画
    if (recyclerView.indexOfChild(targetView) >= 0) {
    int targetX, targetY;

    RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
    int spanCount = ((GridLayoutManager) manager).getSpanCount();
    // 计算移动后的坐标
    // 移动后 高度变化 (我的分页Grid最后一个item在其他分页新的一行第一个)
    if ((myItems.size() - BaseTabListAdapter.COUNT_PRE_MY_HEADER) % spanCount == 0) {
    //获取我的分页最后一个
    View preTargetView = recyclerView.getLayoutManager().findViewByPosition(myItems.size() + BaseTabListAdapter.COUNT_PRE_OTHER_HEADER - 1);
    targetX = preTargetView.getLeft();
    targetY = preTargetView.getTop();
    } else {
    targetX = targetView.getLeft();
    targetY = targetView.getTop();
    }
    //开始执行动画
    startAnimation(recyclerView, currentView, targetX, targetY);
    moveMyToOther(adapter,myHolder,myItems,otherItems);
    } else {
    moveMyToOther(adapter,myHolder,myItems,otherItems);
    }
    }

    //其他分页移动到我的分页
    public static void otherToMy(RecyclerView parent,RecyclerView.Adapter adapter,RecyclerView.ViewHolder otherHolder, List myItems ,List otherItems){
    RecyclerView recyclerView = parent;
    RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
    //获取当前位置
    int currentPiosition = otherHolder.getAdapterPosition();
    // 获取当前位置的view
    View currentView = manager.findViewByPosition(currentPiosition);
    // 获取目标位置的view,我的分页最后一个
    View preTargetView = manager.findViewByPosition(myItems.size() - 1 + BaseTabListAdapter.COUNT_PRE_MY_HEADER);

    // 如果targetView不在屏幕内,则为-1,此时不需要添加动画,因为此时notifyItemMoved自带一个向目标移动的动画
    // 如果在屏幕内,则添加一个位移动画
    if (recyclerView.indexOfChild(preTargetView) >= 0) {
    int targetX = preTargetView.getLeft();
    int targetY = preTargetView.getTop();
    // target 我的分页最后一个
    int targetPosition = myItems.size() - 1 + BaseTabListAdapter.COUNT_PRE_OTHER_HEADER;

    GridLayoutManager gridLayoutManager = ((GridLayoutManager) manager);
    int spanCount = gridLayoutManager.getSpanCount();

    //目标位置在grid的第一个位置
    if ((targetPosition - BaseTabListAdapter.COUNT_PRE_MY_HEADER) % spanCount == 0) {
    View targetView = manager.findViewByPosition(targetPosition);
    targetX = targetView.getLeft();
    targetY = targetView.getTop();
    } else {
    targetX += preTargetView.getWidth();

    // 最后一个item可见时
    if (gridLayoutManager.findLastVisibleItemPosition() == adapter.getItemCount() - 1) {
    // 最后一个item在最后一行第一个位置
    if ((adapter.getItemCount() - 1 - myItems.size() - BaseTabListAdapter.COUNT_PRE_OTHER_HEADER) % spanCount == 0) {
    int firstVisiblePostion = gridLayoutManager.findFirstVisibleItemPosition();
    // 第一个item可见时
    if (firstVisiblePostion == 0) {
    // FirstCompletelyVisibleItemPosition == 0 内容不满一屏幕 , targetY值不需要变化
    // // FirstCompletelyVisibleItemPosition != 0 内容满一屏幕 并且 可滑动 , targetY值 + firstItem.getTop
    if (gridLayoutManager.findFirstCompletelyVisibleItemPosition() != 0) {
    int offset = (-recyclerView.getChildAt(0).getTop()) - recyclerView.getPaddingTop();
    targetY += offset;
    }
    } else {
    // 移动后, targetY值+一个item的高度
    targetY += preTargetView.getHeight();
    }
    }
    } else {
    System.out.println("current--No");
    }
    }

    // 如果当前位置是其他分页的最后一个
    // 并且 当前位置不在grid的第一个位置
    // 并且 目标位置不在grid的第一个位置

    // 则需要延迟250秒notifyItemMove,这种情况,不触发ItemAnimator,会直接刷新界面
    if (currentPiosition == gridLayoutManager.findLastVisibleItemPosition()
    && (currentPiosition - myItems.size() - BaseTabListAdapter.COUNT_PRE_OTHER_HEADER) % spanCount != 0
    && (targetPosition - BaseTabListAdapter.COUNT_PRE_MY_HEADER) % spanCount != 0) {
    moveOtherToMyWithDelay(otherHolder,adapter,myItems,otherItems);
    } else {
    moveOtherToMy(otherHolder,adapter,myItems,otherItems);
    }
    startAnimation(recyclerView, currentView, targetX, targetY);

    } else {
    moveOtherToMy(otherHolder,adapter,myItems,otherItems);
    }
    }

    /**
    * 其他分页移动到我的分页
    *
    * @param otherHolder
    */
    public static void moveOtherToMy(RecyclerView.ViewHolder otherHolder,RecyclerView.Adapter adapter,List myItems,List otherItems) {
    int position = processItemRemoveAdd(otherHolder,myItems,otherItems);
    if (position == -1) {
    return;
    }
    //刷新该位置item
    adapter.notifyItemMoved(position, myItems.size() - 1 + BaseTabListAdapter.COUNT_PRE_MY_HEADER);
    }


    //其他分页移动到我的分页 伴随延迟
    public static void moveOtherToMyWithDelay(RecyclerView.ViewHolder otherHolder, final RecyclerView.Adapter adapter, final List myItems,List otherItems) {
    final int position = processItemRemoveAdd(otherHolder,myItems,otherItems);
    if (position == -1) {
    return;
    }
    delayHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
    //刷新该位置item
    adapter.notifyItemMoved(position, myItems.size() - 1 + BaseTabListAdapter.COUNT_PRE_MY_HEADER);
    }
    }, ANIM_TIME);
    }

    //将其他分页移动到我的分页
    public static int processItemRemoveAdd(RecyclerView.ViewHolder otherHolder, List myItems ,List otherItems) {
    //当前操作的位置
    int position = otherHolder.getAdapterPosition();
    //对应数据集中的位置
    int startPosition = position - myItems.size() - BaseTabListAdapter.COUNT_PRE_OTHER_HEADER;

    if (startPosition > otherItems.size() - 1) {
    return -1;
    }
    //获得移动的item
    Object item = otherItems.get(startPosition);
    //移除数据集中对应的数据
    otherItems.remove(startPosition);
    //添加到末尾
    myItems.add(item);
    return position;
    }


    //我的分页移动到其他分页
    public static void moveMyToOther(RecyclerView.Adapter adapter, RecyclerView.ViewHolder myHolder, List myItems ,List otherItems) {
    //当前操作的位置
    int position = myHolder.getAdapterPosition();
    //对应数据集中的位置
    int startPosition = position - BaseTabListAdapter.COUNT_PRE_MY_HEADER;
    if(startPosition == 0){ //过滤第一个
    return;
    }
    if (startPosition > myItems.size() - 1) {
    return;
    }
    //获得移动的item
    Object item = myItems.get(startPosition);
    //移除数据集中对应的数据
    myItems.remove(startPosition);
    //添加到第一个的位置
    otherItems.add(0, item);
    //刷新该位置item
    adapter.notifyItemMoved(position, myItems.size() + BaseTabListAdapter.COUNT_PRE_OTHER_HEADER);
    }


    /**
    * 启动动画
    */
    public static void startAnimation(RecyclerView recyclerView, final View currentView, float targetX, float targetY) {
    //获取recyclerView的外层view
    final ViewGroup viewGroup = (ViewGroup) recyclerView.getParent();
    final ImageView mirrorView = addMirrorView(viewGroup, recyclerView, currentView);

    Animation animation = getTranslateAnimator(
    targetX - currentView.getLeft(), targetY - currentView.getTop());
    //隐藏当前view
    currentView.setVisibility(View.INVISIBLE);
    //镜像view开始动画
    mirrorView.startAnimation(animation);
    // 动画监听
    animation.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
    }

    @Override
    public void onAnimationEnd(Animation animation) {
    //动画结束,移除镜像view
    viewGroup.removeView(mirrorView);
    //当前view不可见时让其可见
    if (currentView.getVisibility() == View.INVISIBLE) {
    currentView.setVisibility(View.VISIBLE);
    }
    }

    @Override
    public void onAnimationRepeat(Animation animation) {

    }
    });
    }

    //位移动画
    public static TranslateAnimation getTranslateAnimator(float targetX, float targetY) {
    TranslateAnimation translateAnimation = new TranslateAnimation(
    Animation.RELATIVE_TO_SELF, 0f,
    Animation.ABSOLUTE, targetX,
    Animation.RELATIVE_TO_SELF, 0f,
    Animation.ABSOLUTE, targetY);
    // RecyclerView默认移动动画250ms 这里设置360ms 是为了防止在位移动画结束后 remove(view)过早 导致闪烁
    translateAnimation.setDuration(ANIM_TIME);
    translateAnimation.setFillAfter(true);
    return translateAnimation;
    }

    //添加需要移动的 镜像View
    public static ImageView addMirrorView(ViewGroup parent, RecyclerView recyclerView, View view) {
    //把旧的cache销毁
    view.destroyDrawingCache();
    //通过setDrawingCacheEnable方法开启cache
    view.setDrawingCacheEnabled(true);
    //创建镜像view
    ImageView mirrorView = new ImageView(recyclerView.getContext());
    //调用getDrawingCache方法就可以获得view的cache图片
    Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
    mirrorView.setImageBitmap(bitmap);
    view.setDrawingCacheEnabled(false);
    int[] locations = new int[2];
    //获取view的位置到locations
    view.getLocationOnScreen(locations);
    int[] parenLocations = new int[2];
    //获取recyclerView的位置到parenLocations
    recyclerView.getLocationOnScreen(parenLocations);
    //设置布局的margin值
    FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(bitmap.getWidth(), bitmap.getHeight());
    params.setMargins(locations[0], locations[1] - parenLocations[1], 0, 0);
    //在动画起始处添加镜像view
    parent.addView(mirrorView, params);

    return mirrorView;
    }

    }

    最后,封装一下BaseTabListAdapter,定义部分常量,重写通用的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    public class BaseTabListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements OnItemMoveListener {
    // 类型定义
    public static final int TYPE_MY_HEADER = 0; // 我的分页标题
    public static final int TYPE_MY = 1; // 我的分页
    public static final int TYPE_OTHER_HEADER = 2; // 其他分页标题
    public static final int TYPE_OTHER = 3; // 其他分页

    // 数量定义
    protected static final int COUNT_PRE_MY_HEADER = 1; // 我的分页之前header数量
    protected static final int COUNT_PRE_OTHER_HEADER = COUNT_PRE_MY_HEADER + 1; // 其他分页之前header数量

    protected long startTime; // touch点击开始时间
    protected static final long SPACE_TIME = 100; // touch间隔时间 与"点击"区分

    protected boolean isEditMode; //编辑模式标志
    protected ItemTouchHelper mItemTouchHelper;
    protected OnMyItemClickListener onMyItemClickListener;// 我的分页点击事件

    private List myItems;
    private List otherItems;

    public BaseTabListAdapter(List myItems, List otherItems) {
    this.myItems = myItems;
    this.otherItems = otherItems;
    }

    public interface OnMyItemClickListener {
    void onItemClick(View v, int position);
    }

    public void setOnMyItemClickListener(OnMyItemClickListener listener) {
    this.onMyItemClickListener = listener;
    }
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
    // 我的分页标题 + 我的分页.size + 其他分页标题 + 其他分页.size
    return myItems.size() + otherItems.size() + BaseTabListAdapter.COUNT_PRE_OTHER_HEADER;
    }

    @Override
    public void onItemMove(int fromPosition, int toPosition) {
    //过滤第一个,并且也不能移动到第一个去
    if(fromPosition == 1 || toPosition == 1){
    return;
    }
    //得到当前移动的item
    Object item = myItems.get(fromPosition - BaseTabListAdapter.COUNT_PRE_MY_HEADER);
    //删除数据集中的对应数据
    myItems.remove(fromPosition - BaseTabListAdapter.COUNT_PRE_MY_HEADER);
    //添加数据到移动后的位置
    myItems.add(toPosition - BaseTabListAdapter.COUNT_PRE_MY_HEADER, item);
    //开始刷新
    notifyItemMoved(fromPosition, toPosition);
    }

    @Override
    public int getItemViewType(int position) {
    if (position == 0) { // 我分页标题
    return BaseTabListAdapter.TYPE_MY_HEADER;
    } else if (position == myItems.size() + 1) { // 其他分页标题
    return BaseTabListAdapter.TYPE_OTHER_HEADER;
    } else if (position > 0 && position < myItems.size() + 1) { //我的分页
    return BaseTabListAdapter.TYPE_MY;
    } else { //其他分页
    return BaseTabListAdapter.TYPE_OTHER;
    }
    }
    }

    Lib库的使用

需继承BaseTabListAdapter,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
public class TabListAdapter  extends BaseTabListAdapter{

private LayoutInflater mInflater;
private List<MainTitleDTO> myItems, otherItems;//我的tab项,其余tab项

public TabListAdapter(Context context, ItemTouchHelper helper, List<MainTitleDTO> myItems, List<MainTitleDTO> otherItems) {
super(myItems,otherItems);
this.mInflater = LayoutInflater.from(context);
this.mItemTouchHelper = helper;
this.myItems = myItems;
this.otherItems = otherItems;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
final View view;
switch (viewType) {
case BaseTabListAdapter.TYPE_MY_HEADER://我的分页标题
view = mInflater.inflate(R.layout.item_my_header, parent, false);
final MyHeaderViewHolder holder = new MyHeaderViewHolder(view);
holder.tvBtnEdit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isEditMode) {
startEditMode((RecyclerView) parent);
holder.tvBtnEdit.setText(R.string.finish);
} else {
cancelEditMode((RecyclerView) parent);
holder.tvBtnEdit.setText(R.string.edit);
}
}
});
return holder;
case BaseTabListAdapter.TYPE_MY: //我的分页
view = mInflater.inflate(R.layout.item_my, parent, false);
final MyViewHolder myHolder = new MyViewHolder(view);
//item点击监听
myHolder.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
int position = myHolder.getAdapterPosition();
//编辑模式
if (isEditMode) {
//执行移动到其他分页的逻辑和动画
RecyclerviewAnimator.myToOther(position,(RecyclerView) parent,TabListAdapter.this,myHolder,myItems,otherItems);

} else {
//执行点击事件回调
onMyItemClickListener.onItemClick(v, position - BaseTabListAdapter.COUNT_PRE_MY_HEADER);
}

}
});
//长按监听
myHolder.textView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(final View v) {
//非编辑模式下
if (!isEditMode) {
RecyclerView recyclerView = ((RecyclerView) parent);
//启动开始编辑
startEditMode(recyclerView);

// header 按钮文字 改成 "完成"
View view = recyclerView.getChildAt(0);
if (view == recyclerView.getLayoutManager().findViewByPosition(0)) {
TextView tvBtnEdit = (TextView) view.findViewById(R.id.tv_btn_edit);
tvBtnEdit.setText(R.string.finish);
}
}
//开始拖拽
mItemTouchHelper.startDrag(myHolder);
return true;
}
});
//触摸监听
myHolder.textView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (isEditMode) {
int position = myHolder.getAdapterPosition();
if(position == 1){//过滤第一个
return false;
}
switch (MotionEventCompat.getActionMasked(event)) {
case MotionEvent.ACTION_DOWN:
//按下时间记录
startTime = System.currentTimeMillis();
break;
case MotionEvent.ACTION_MOVE:
//如果当前时间-按下时间大于间隔时间,判定为拖拽
if (System.currentTimeMillis() - startTime > BaseTabListAdapter.SPACE_TIME) {
mItemTouchHelper.startDrag(myHolder);
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
startTime = 0;
break;
}

}
return false;
}
});
return myHolder;
case BaseTabListAdapter.TYPE_OTHER_HEADER: //其他分页标题
view = mInflater.inflate(R.layout.item_other_header, parent, false);
return new RecyclerView.ViewHolder(view) {
};
case BaseTabListAdapter.TYPE_OTHER: //其他分页
view = mInflater.inflate(R.layout.item_other, parent, false);
final OtherViewHolder otherHolder = new OtherViewHolder(view);
otherHolder.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//不管什么模式,都执行移动到我的分页逻辑和动画
RecyclerviewAnimator.otherToMy((RecyclerView) parent,TabListAdapter.this,otherHolder,myItems,otherItems);
}
});
return otherHolder;
}
return null;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//我的分页
if (holder instanceof MyViewHolder) {
MyViewHolder myHolder = (MyViewHolder) holder;
myHolder.textView.setText(myItems.get(position - BaseTabListAdapter.COUNT_PRE_MY_HEADER).getTitle());
if(position == 1){//过滤第一个,字体颜色更改
myHolder.textView.setTextColor(Color.GRAY);
}
if (isEditMode) {
myHolder.imgEdit.setVisibility(View.VISIBLE);
} else {
myHolder.imgEdit.setVisibility(View.INVISIBLE);
}

}
//其他分页
else if (holder instanceof OtherViewHolder) {

((OtherViewHolder) holder).textView.setText(otherItems.get(position - myItems.size() - BaseTabListAdapter.COUNT_PRE_OTHER_HEADER).getTitle());

}
//我的分页标题
else if (holder instanceof MyHeaderViewHolder) {

MyHeaderViewHolder headerHolder = (MyHeaderViewHolder) holder;
if (isEditMode) {
headerHolder.tvBtnEdit.setText(R.string.finish);
} else {
headerHolder.tvBtnEdit.setText(R.string.edit);
}
}
}


//开启编辑
protected void startEditMode(RecyclerView parent) {
isEditMode = true; //更新标志
//得到item数量
int visibleChildCount = parent.getChildCount();
//遍历item的view
for (int i = 0; i < visibleChildCount; i++) {
View view = parent.getChildAt(i);
if(i == 1){//过滤第一个,屏蔽点击事件
TextView textView = (TextView) view.findViewById(R.id.tv);
textView.setTextColor(Color.GRAY);
textView.setClickable(false);
textView.setEnabled(false);
}else { //设置右上角按钮可见
ImageView imgEdit = (ImageView) view.findViewById(R.id.img_edit);
if (imgEdit != null) {
imgEdit.setVisibility(View.VISIBLE);
}
}

}
}

//完成编辑
protected void cancelEditMode(RecyclerView parent) {
isEditMode = false;//更新标志
//得到item数量
int visibleChildCount = parent.getChildCount();
//遍历item的view
for (int i = 0; i < visibleChildCount; i++) {
View view = parent.getChildAt(i);
if(i == 1){ //过滤第一个,恢复点击事件
TextView textView = (TextView) view.findViewById(R.id.tv);
textView.setTextColor(Color.GRAY);
textView.setClickable(true);
textView.setEnabled(true);
}else { //设置右上角按钮不可见
ImageView imgEdit = (ImageView) view.findViewById(R.id.img_edit);
if (imgEdit != null) {
imgEdit.setVisibility(View.INVISIBLE);
}
}
}
}

//我的分页holder
public static class MyViewHolder extends RecyclerView.ViewHolder implements OnItemDragListener {
private TextView textView;
private ImageView imgEdit;

public MyViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tv);
imgEdit = itemView.findViewById(R.id.img_edit);
}

//item 被选中时
@Override
public void onItemSelected() {
textView.setBackgroundResource(R.drawable.bg_channel_p);
}

//item 取消选中时
@Override
public void onItemFinish() {
textView.setBackgroundResource(R.drawable.bg_channel);
}
}

//其他分页holder
public static class OtherViewHolder extends RecyclerView.ViewHolder {
private TextView textView;

public OtherViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tv);
}
}

//我的分页标题holder
public static class MyHeaderViewHolder extends RecyclerView.ViewHolder {
private TextView tvBtnEdit;

public MyHeaderViewHolder(View itemView) {
super(itemView);
tvBtnEdit = itemView.findViewById(R.id.tv_btn_edit);
}
}
}

初始化recyclerview,实现item点击切换到该页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void init() {
GridLayoutManager manager = new GridLayoutManager(this, 4);
mRecyclerView.setLayoutManager(manager);

ItemDragHelperCallback callback = new ItemDragHelperCallback();
final ItemTouchHelper helper = new ItemTouchHelper(callback);
helper.attachToRecyclerView(mRecyclerView);

final TabListAdapter adapter = new TabListAdapter(this, helper, myTabs, otherTabs);
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int viewType = adapter.getItemViewType(position);
return viewType == BaseTabListAdapter.TYPE_MY || viewType == BaseTabListAdapter.TYPE_OTHER ? 1 : 4;
}
});
mRecyclerView.setAdapter(adapter);

adapter.setOnMyItemClickListener(new BaseTabListAdapter.OnMyItemClickListener() {
@Override
public void onItemClick(View v, int position) {
Intent intent=new Intent();
if(isUpdate()){
intent.putExtra(MY_CHANNEL, myTabs);
intent.putExtra(OTHER_CHANNEL, otherTabs);
}

intent.putExtra(MainActivity.GOTO_FRAGMENT,position);
setResult(RESULT_CODE,intent);
finish();
}
});
}

这里要回到主界面,需要判断一下,是否有item调整,数据更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//数据是否改变,有无操作
private boolean isUpdate(){
if(lastTabs.size()!= myTabs.size()){
return true;
}else{
for (int i = 0; i < lastTabs.size(); i++) {
if(lastTabs.get(i).getId()!= myTabs.get(i).getId()){
return true;
}
}

}
return false;
}

读取主界面传过来的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_manage);
if (getIntent().hasExtra(MY_CHANNEL)) {
ArrayList curTabs = (ArrayList) getIntent().getSerializableExtra(MY_CHANNEL);

this.lastTabs.addAll(curTabs);
this.myTabs.addAll(curTabs);
}
if (getIntent().hasExtra(OTHER_CHANNEL)) {
ArrayList otherChannels = (ArrayList) getIntent().getSerializableExtra(OTHER_CHANNEL);

this.otherTabs.addAll(otherChannels);
}
init();

}

关闭按钮的监听

1
2
3
4
5
6
7
8
9
10
11
12
13
@OnClick({R.id.icon_collapse})
public void onEventClick(View view) {
switch (view.getId()) {
case R.id.icon_collapse://退出监听

Intent intent=new Intent();
intent.putExtra(MY_CHANNEL, myTabs);
intent.putExtra(OTHER_CHANNEL, otherTabs);
setResult(RESULT_CODE,intent);
finish();
break;
}
}

主界面处理

接收tab管理页传过来的值,刷新界面和跳转到对应页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) {
if (resultCode == TabManageActivity.RESULT_CODE) {
if (data.hasExtra(MY_CHANNEL) && data.hasExtra(OTHER_CHANNEL)) {

if (data.hasExtra(MY_CHANNEL)) {
items.clear();
ArrayList myChannels = (ArrayList) data.getSerializableExtra(MY_CHANNEL);
items.addAll(myChannels);
}
if (data.hasExtra(OTHER_CHANNEL)) {
otherItems.clear();
ArrayList otherChannels = (ArrayList) data.getSerializableExtra(OTHER_CHANNEL);
otherItems.addAll(otherChannels);
}
setTabTitle(false);
mExamplePagerAdapter.notifyDataSetChanged();
}
if (data.hasExtra(GOTO_FRAGMENT)) {
int index = data.getIntExtra(GOTO_FRAGMENT, 0);
gotoFragment(index);
}

}
}
}

以上就是实现调整tab分组和顺序的全过程,如果需要查看源码的可以访问我的github,这里是[传送门][5],别忘了点下star哦~
[1]: https://github.com/hackware1993/MagicIndicator
[5]:https://github.com/jessieeeee/SelectLabelTab