Android 仿今日头条频道管理(GridView之间Item的移动和拖拽)

很多博客上都有类似今日头条的一些案例,频道管理GridView之间的拖拽移动,flowtaglayout taglayout  流式布局等不相上下,下面我们来看看这次的效果:

 

上图是2个gridview组成、2个gridview之间的Item是可以相互更换的、而且我的频道的Item是可以拖拽进行排序。仔细观察、今日头条有些细节做的的非常好,当一个gridview1的item移动到另一个gridview2时、gridview1的item不会立即消失、而是有一个没有内容的背景框、等item移动gridview2操作完毕才会消失、并且gridview2在gridview1的Item到达之前也有一个没有内容的背景框,等到达后覆盖。给用户一个很强的交互体验,很赞。而且在item拖拽移动排序时、拖拽的item会变大变色、效果很赞,体验极好。感兴趣的话可以下一个今日头条的客户端看看。当看到这么赞的交互体验,我就想看看是怎么实现的,这篇博客先讲2个Gridview之间item的移动。

实现思路:

 

1、首先我们获取我们点击的位置、处于gridview哪个位置

2、获取位置后、我们就能拿到这个Item的View,我们获取item绘制缓存的Bitmap对象。

3、将Bitmap设置的一个Imageview上,然后将这个ImageView放到一个容器中去进行移动操作,这样可能有人有疑问,为什么不直接把item放到容器中去呢,是因为item已经有自己的父容器gridview,所以我们new一个Imageview来代替item

4、然后我们将imageview移动到另一个gridview的最后一个位置。

5、最后刷新2个gridview的视图、就能实现我们所见的效果。

实现代码:

1.MainActivity

import android.graphics.Bitmap;  
import android.os.Bundle;  
import android.os.Handler;  
import android.support.v7.app.AppCompatActivity;  
import android.view.View;  
import android.view.ViewGroup;  
import android.view.animation.Animation;  
import android.view.animation.AnimationSet;  
import android.view.animation.TranslateAnimation;  
import android.widget.AdapterView;  
import android.widget.AdapterView.OnItemClickListener;  
import android.widget.GridView;  
import android.widget.ImageView;  
import android.widget.LinearLayout;  
import android.widget.TextView;  
  
import com.test.drag.view.MyGridView;  
  
import java.util.ArrayList;  
import java.util.List;  
  
public class MainActivity extends AppCompatActivity implements OnItemClickListener {  
    private MyGridView mUserGv, mOtherGv;  
    private List<String> mUserList = new ArrayList<>();  
    private List<String> mOtherList = new ArrayList<>();  
    private OtherAdapter mUserAdapter, mOtherAdapter;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        initView();  
    }  
  
    public void initView() {  
        mUserGv = (MyGridView) findViewById(R.id.userGridView);  
        mOtherGv = (MyGridView) findViewById(R.id.otherGridView);  
        mUserList.add("推荐");  
        mUserList.add("热点");  
        mUserList.add("上海");  
        mUserList.add("时尚");  
        mUserList.add("科技");  
        mUserList.add("体育");  
        mUserList.add("军事");  
        mUserList.add("财经");  
        mUserList.add("网络");  
        mOtherList.add("汽车");  
        mOtherList.add("房产");  
        mOtherList.add("社会");  
        mOtherList.add("情感");  
        mOtherList.add("女人");  
        mOtherList.add("旅游");  
        mOtherList.add("健康");  
        mOtherList.add("美女");  
        mOtherList.add("游戏");  
        mOtherList.add("数码");  
        mOtherList.add("娱乐");  
        mOtherList.add("探索");  
        mUserAdapter = new OtherAdapter(this, mUserList,true);  
        mOtherAdapter = new OtherAdapter(this, mOtherList,false);  
        mUserGv.setAdapter(mUserAdapter);  
        mOtherGv.setAdapter(mOtherAdapter);  
        mUserGv.setOnItemClickListener(this);  
        mOtherGv.setOnItemClickListener(this);  
    }  
  
    /**  
     *获取点击的Item的对应View,  
     *因为点击的Item已经有了自己归属的父容器MyGridView,所有我们要是有一个ImageView来代替Item移动  
     * @param view  
     * @return  
     */  
    private ImageView getView(View view) {  
        view.destroyDrawingCache();  
        view.setDrawingCacheEnabled(true);  
        Bitmap cache = Bitmap.createBitmap(view.getDrawingCache());  
        view.setDrawingCacheEnabled(false);  
        ImageView iv = new ImageView(this);  
        iv.setImageBitmap(cache);  
        return iv;  
    }  
    /**  
     * 获取移动的VIEW,放入对应ViewGroup布局容器  
     * @param viewGroup  
     * @param view  
     * @param initLocation  
     * @return  
     */  
    private View getMoveView(ViewGroup viewGroup, View view, int[] initLocation) {  
        int x = initLocation[0];  
        int y = initLocation[1];  
        viewGroup.addView(view);  
        LinearLayout.LayoutParams mLayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);  
        mLayoutParams.leftMargin = x;  
        mLayoutParams.topMargin = y;  
        view.setLayoutParams(mLayoutParams);  
        return view;  
    }  
  
    /**  
     * 创建移动的ITEM对应的ViewGroup布局容器  
     * 用于存放我们移动的View  
     */  
    private ViewGroup getMoveViewGroup() {  
        //window中最顶层的view  
        ViewGroup moveViewGroup = (ViewGroup) getWindow().getDecorView();  
        LinearLayout moveLinearLayout = new LinearLayout(this);  
        moveLinearLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));  
        moveViewGroup.addView(moveLinearLayout);  
        return moveLinearLayout;  
    }  
    /**  
     * 点击ITEM移动动画  
     *  
     * @param moveView  
     * @param startLocation  
     * @param endLocation  
     * @param moveChannel  
     * @param clickGridView  
     */  
    private void MoveAnim(View moveView, int[] startLocation, int[] endLocation, final String moveChannel,  
                          final GridView clickGridView, final boolean isUser) {  
        int[] initLocation = new int[2];  
        //获取传递过来的VIEW的坐标  
        moveView.getLocationInWindow(initLocation);  
        //得到要移动的VIEW,并放入对应的容器中  
        final ViewGroup moveViewGroup = getMoveViewGroup();  
        final View mMoveView = getMoveView(moveViewGroup, moveView, initLocation);  
        //创建移动动画  
        TranslateAnimation moveAnimation = new TranslateAnimation(  
                startLocation[0], endLocation[0], startLocation[1],  
                endLocation[1]);  
        moveAnimation.setDuration(300L);//动画时间  
        //动画配置  
        AnimationSet moveAnimationSet = new AnimationSet(true);  
        moveAnimationSet.setFillAfter(false);//动画效果执行完毕后,View对象不保留在终止的位置  
        moveAnimationSet.addAnimation(moveAnimation);  
        mMoveView.startAnimation(moveAnimationSet);  
        moveAnimationSet.setAnimationListener(new Animation.AnimationListener() {  
  
            @Override  
            public void onAnimationStart(Animation animation) {  
            }  
  
            @Override  
            public void onAnimationRepeat(Animation animation) {  
            }  
  
            @Override  
            public void onAnimationEnd(Animation animation) {  
                moveViewGroup.removeView(mMoveView);  
                // 判断点击的是DragGrid还是OtherGridView  
                if (isUser) {  
                    mOtherAdapter.setVisible(true);  
                    mOtherAdapter.notifyDataSetChanged();  
                    mUserAdapter.remove();  
                } else {  
                    mUserAdapter.setVisible(true);  
                    mUserAdapter.notifyDataSetChanged();  
                    mOtherAdapter.remove();  
                }  
            }  
        });  
    }  
  
    @Override  
    public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {  
        switch (parent.getId()) {  
            case R.id.userGridView:  
                //position为 0,1 的不可以进行任何操作  
                if (position != 0 && position != 1) {  
                    final ImageView moveImageView = getView(view);  
                    if (moveImageView != null) {  
                        TextView newTextView = (TextView) view.findViewById(R.id.text_item);  
                        final int[] startLocation = new int[2];  
                        newTextView.getLocationInWindow(startLocation);  
                        final String channel = ((OtherAdapter) parent.getAdapter()).getItem(position);//获取点击的频道内容  
                        mOtherAdapter.setVisible(false);  
                        //添加到最后一个  
                        mOtherAdapter.addItem(channel);  
                        new Handler().postDelayed(new Runnable() {  
                            public void run() {  
                                try {  
                                    int[] endLocation = new int[2];  
                                    //获取终点的坐标  
                                    mOtherGv.getChildAt(mOtherGv.getLastVisiblePosition()).getLocationInWindow(endLocation);  
                                    MoveAnim(moveImageView, startLocation, endLocation, channel, mUserGv, true);  
                                    mUserAdapter.setRemove(position);  
                                } catch (Exception localException) {  
                                }  
                            }  
                        }, 50L);  
                    }  
                }  
                break;  
            case R.id.otherGridView:  
                final ImageView moveImageView = getView(view);  
                if (moveImageView != null) {  
                    TextView newTextView = (TextView) view.findViewById(R.id.text_item);  
                    final int[] startLocation = new int[2];  
                    newTextView.getLocationInWindow(startLocation);  
                    final String channel = ((OtherAdapter) parent.getAdapter()).getItem(position);  
                    mUserAdapter.setVisible(false);  
                    //添加到最后一个  
                    mUserAdapter.addItem(channel);  
                    new Handler().postDelayed(new Runnable() {  
                        public void run() {  
                            try {  
                                int[] endLocation = new int[2];  
                                //获取终点的坐标  
                                mUserGv.getChildAt(mUserGv.getLastVisiblePosition()).getLocationInWindow(endLocation);  
                                MoveAnim(moveImageView, startLocation, endLocation, channel, mOtherGv,false);  
                                mOtherAdapter.setRemove(position);  
                            } catch (Exception localException) {  
                            }  
                        }  
                    }, 50L);  
                }  
                break;  
            default:  
                break;  
        }  
    }  
}  

2.适配器:

import android.content.Context;  
import android.view.LayoutInflater;  
import android.view.View;  
import android.view.ViewGroup;  
import android.widget.BaseAdapter;  
import android.widget.TextView;  
  
import java.util.List;  
  
/**  
 * Created by fuweiwei on 2016/1/8.  
 */  
public class OtherAdapter extends BaseAdapter {  
  
    private Context context;  
    public List<String> channelList;  
    private TextView item_text;  
    /** 是否可见 在移动动画完毕之前不可见,动画完毕后可见*/  
    boolean isVisible = true;  
    /** 要删除的position */  
    public int remove_position = -1;  
    /** 是否是用户频道 */  
    private boolean isUser = false;  
  
    public OtherAdapter(Context context, List<String> channelList ,boolean isUser) {  
        this.context = context;  
        this.channelList = channelList;  
        this.isUser = isUser;  
    }  
  
    @Override  
    public int getCount() {  
        return channelList == null ? 0 : channelList.size();  
    }  
  
    @Override  
    public String getItem(int position) {  
        if (channelList != null && channelList.size() != 0) {  
            return channelList.get(position);  
        }  
        return null;  
    }  
  
    @Override  
    public long getItemId(int position) {  
        return position;  
    }  
  
    @Override  
    public View getView(int position, View convertView, ViewGroup parent) {  
        View view = LayoutInflater.from(context).inflate(R.layout.adapter_mygridview_item, null);  
        item_text = (TextView) view.findViewById(R.id.text_item);  
        String channel = getItem(position);  
        item_text.setText(channel);  
        if(isUser){  
            if ((position == 0) || (position == 1)){  
                item_text.setEnabled(false);  
            }  
        }  
        if (!isVisible && (position == -1 + channelList.size())){  
            item_text.setText("");  
            item_text.setSelected(true);  
            item_text.setEnabled(true);  
        }  
        if(remove_position == position){  
            item_text.setText("");  
        }  
        return view;  
    }  
  
    /** 获取频道列表 */  
    public List<String> getChannnelLst() {  
        return channelList;  
    }  
  
    /** 添加频道列表 */  
    public void addItem(String channel) {  
        channelList.add(channel);  
        notifyDataSetChanged();  
    }  
  
    /** 设置删除的position */  
    public void setRemove(int position) {  
        remove_position = position;  
        notifyDataSetChanged();  
        // notifyDataSetChanged();  
    }  
  
    /** 删除频道列表 */  
    public void remove() {  
        channelList.remove(remove_position);  
        remove_position = -1;  
        notifyDataSetChanged();  
    }  
    /** 设置频道列表 */  
    public void setListDate(List<String> list) {  
        channelList = list;  
    }  
  
    /** 获取是否可见 */  
    public boolean isVisible() {  
        return isVisible;  
    }  
  
    /** 设置是否可见 */  
    public void setVisible(boolean visible) {  
        isVisible = visible;  
    }  
  

3.自定义GridView:

import android.content.Context;  
import android.util.AttributeSet;  
import android.widget.GridView;  
  
/**  
 * Created by fuweiwei on 2016/1/8.  
 */  
public class MyGridView extends GridView {  
    public MyGridView(Context paramContext, AttributeSet paramAttributeSet) {  
        super(paramContext, paramAttributeSet);  
    }  
  
    @Override  
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,  
                MeasureSpec.AT_MOST);  
        super.onMeasure(widthMeasureSpec, expandSpec);  
    }  
}  

4.主函数布局:

<?xml version="1.0" encoding="utf-8"?>  
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"  
    android:layout_height="match_parent" tools:context=".MainActivity">  
    <ScrollView  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"  
        >  
  
        <LinearLayout  
            android:id="@+id/subscribe_main_layout"  
            android:layout_width="fill_parent"  
            android:layout_height="wrap_content"  
            android:orientation="vertical"  
            android:paddingBottom="14.0dip" >  
  
            <LinearLayout  
                android:layout_width="fill_parent"  
                android:layout_height="wrap_content"  
                android:layout_marginLeft="10.0dip"  
                android:layout_marginTop="14.0dip"  
                android:gravity="bottom"  
                android:orientation="horizontal" >  
  
  
                <TextView  
                    android:id="@+id/my_category_tip_text"  
                    android:layout_width="wrap_content"  
                    android:layout_height="wrap_content"  
                    android:textSize="13sp"  
                    android:text="我的频道"  
                    />  
            </LinearLayout>  
  
            <View  
                android:id="@+id/seperate_line"  
                android:layout_width="match_parent"  
                android:layout_marginTop="10dp"  
                android:layout_height="0.5dp"  
                android:layout_marginBottom="14.0dip"  
                android:background="@color/subscribe_item_drag_stroke" />  
  
            <com.test.drag.view.MyGridView  
                android:id="@+id/userGridView"  
                android:layout_width="fill_parent"  
                android:layout_height="wrap_content"  
                android:layout_marginLeft="14dip"  
                android:layout_marginRight="14dip"  
                android:gravity="center"  
                android:horizontalSpacing="14dip"  
                android:listSelector="@android:color/transparent"  
                android:numColumns="4"  
                android:scrollbars="vertical"  
                android:stretchMode="columnWidth"  
                android:verticalSpacing="14.0px" />  
  
            <View  
                android:id="@+id/seperate_line2"  
                android:layout_marginTop="10dp"  
                android:layout_width="match_parent"  
                android:layout_height="0.5dp"  
                android:background="@color/subscribe_item_drag_stroke"/>  
  
            <TextView  
                android:id="@+id/more_category_text"  
                android:layout_marginBottom="14.0dip"  
                android:layout_width="wrap_content"  
                android:layout_marginTop="10dp"  
                android:layout_height="wrap_content"  
                android:textSize="13sp"  
                android:layout_marginLeft="10.0dip"  
                android:text="更多频道" />  
  
            <com.test.drag.view.MyGridView  
                android:id="@+id/otherGridView"  
                android:layout_width="fill_parent"  
                android:layout_height="wrap_content"  
                android:layout_marginLeft="14dip"  
                android:layout_marginRight="14dip"  
                android:gravity="center"  
                android:horizontalSpacing="14dip"  
                android:listSelector="@android:color/transparent"  
                android:numColumns="4"  
                android:scrollbars="vertical"  
                android:stretchMode="columnWidth"  
                android:verticalSpacing="14.0px" />  
        </LinearLayout>  
    </ScrollView>  
  
</RelativeLayout>  

5.背景色:subscribe_item_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<selector
  xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="true" android:state_selected="true">
        <layer-list>
            <item>
                <shape>
                    <stroke android:width="1.0dip" android:color="@color/subscribe_item_drag_stroke" android:dashWidth="4.0dip" android:dashGap="2.0dip" />
                    <solid android:color="@color/subscribe_item_drag_bg" />
                </shape>
            </item>
        </layer-list>
    </item>
    <item android:state_selected="true">
        <shape>
            <stroke android:width="1.0dip" android:color="@color/subscribe_item_selected_stroke" />
            <solid android:color="@color/subscribe_item_selected_bg" />
        </shape>
    </item>
    <item android:state_enabled="false">
        <shape>
            <stroke android:width="0.5dip" android:color="@color/subscribe_item_disabled_stroke" />
            <solid android:color="@color/subscribe_item_disabled_bg" />
        </shape>
    </item>
    <item android:state_pressed="true">
        <shape>
            <stroke android:width="0.5dip" android:color="@color/subscribe_item_pressed_stroke" />
            <solid android:color="@color/subscribe_item_pressed_bg" />
        </shape>
    </item>
    <item>
        <shape>
            <stroke android:width="0.5dip" android:color="@color/subscribe_item_normal_stroke" />
            <solid android:color="@color/subscribe_item_normal_bg" />
        </shape>
    </item>
</selector>

6.适配器布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_subscribe"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:minHeight="38.0dip"
    android:minWidth="72.0dip" >

    <TextView
        android:id="@+id/text_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@drawable/subscribe_item_bg"
        android:gravity="center"
        android:minHeight="38.0dip"
        android:minWidth="72.0dip"
        android:textColor="@color/subscribe_item_text_color"
        android:textSize="14.0sp" />
    <!-- android:layout_margin="5dip" -->

    <ImageView
        android:id="@+id/icon_new"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone" />

</RelativeLayout>

实现效果如下:

相关免费源码 :

实现类似网易新闻频道管理功能,包含UI和数据库

https://github.com/zhuguohui/ChannelMangerDemo

自定义GridLayout控件,实现新闻频道等拖拽管理的炫酷功能

https://github.com/JackChan1999/DragGridLayout

一个新闻频道管理view

https://github.com/yilylong/ChannelTagView

 

HandyGridView是一个高仿支付宝,网易新闻的高性能的标签可拖动排序的GridView。

https://github.com/huxq17/HandyGridView

这是一个用来查看GitHub最受欢迎与最热项目的App,它基于React Native支持Android和iOS双平台

https://github.com/qiyei2015/GitHubPopular

 

项目常用案例链接:

类似QQ空间,微信朋友圈,微博主页等,展示图片的九宫格控件,自动根据图片的数量确定图片大小和控件大小,使用Adapter模式设置图片,对外提供接口回调,使用接口加载图片,支持任意的图片加载框架,如 Glide,ImageLoader,Fresco,xUtils3,Picasso 等,支持点击图片全屏预览大图。

https://github.com/jeasonlzy/NineGridView

完全仿微信的图片选择,并且提供了多种图片加载接口,选择图片后可以旋转,可以裁剪成矩形或圆形,可以配置各种其他的参数

https://github.com/jeasonlzy/ImagePicker

 Android实现Http标准协议框架,支持多种缓存模式,底层可动态切换OkHttp、URLConnection。

https://github.com/yanzhenjie/NoHttp

https://github.com/square/retrofit

OkGo - 3.0 震撼来袭,该库是基于 Http 协议,封装了 OkHttp 的网络请求框架,比 Retrofit 更简单易用,支持 RxJava,RxJava2,支持自定义缓存,支持批量断点下载管理和批量上传管理功能

https://github.com/qiyei2015/okhttp-OkGo

一个能让你了解所有函数调用顺序以及函数耗时的Android库(无需侵入式代码)

https://github.com/qiyei2015/AppMethodOrder

RecyclerView下拉刷新,自动加载更多;仿IOS侧滑Item删除菜单

https://github.com/qiyei2015/LRecyclerView

https://github.com/shycdw/BlogDemo

https://github.com/mcxtzhang/SwipeDelMenuLayout

https://github.com/yanjunhui2014/SlidingDeleteView

基于RecyclerView的自定义侧滑删除

https://github.com/asendiLin/SlidesRecyclerView

https://github.com/chthai64/SwipeRevealLayout

https://github.com/daimajia/AndroidSwipeLayout

https://github.com/yanzhenjie/SwipeRecyclerView

https://github.com/HoneyNeutrons/RecyclerViewUndoSwipe

https://github.com/hanshengjian/recyclerviewdemo

https://github.com/Kyogirante/MaterialDesignDemo

万能刷新加载控件

https://github.com/qiyei2015/NRecyclerView 

一个提供设计新闻和灵感的Android应用程序,同时也是实现材料设计的示例。

https://github.com/qiyei2015/plaid

一个惊人的,极简的阅读应用

https://github.com/qiyei2015/LookLook

实现可在多种外形尺寸下工作的音频媒体应用程序,并在Android手机,平板电脑,自动,穿戴式和Cast设备上提供一致的用户体验

https://github.com/qiyei2015/android-UniversalMusicPlayer

gank.io非官方客户端项目,RxJava和Retrofit

https://github.com/qiyei2015/Meizhi

Android RecyclerView.LayoutManager根据SpanSize调整视图的大小并重新排序

https://github.com/Arasthel/SpannedGridLayoutManager

关于RxJava

https://github.com/yilylong/RXjavaDemo

向左滑动出现负一屏的自定义ViewGroup

https://github.com/yilylong/SecondaryScreen

 

Android RecyclerView(超简单)实现可展开列表——单项展开

https://github.com/monkeyLittleMonkey/ExpandRecyclerViewDemo

https://github.com/xzfxzf1314/expandedrecycleviewdemo/tree/master

自定义炫酷的RecyclerView(自定义LayoutManager)的LayoutManager(页卡滑动,类似探探左右滑动,上下滑动,抖音上下滑动,时间选择滑动,轮播图滑动等效果)

https://github.com/DingMouRen/LayoutManagerGroup

模仿探探卡片滑动效果的布局

https://github.com/yuqirong/CardSwipeLayout

Bamboy 各种Demo合集

https://github.com/Bamboy120315/bamboy

https://github.com/gabrielemariotti/RecyclerViewItemAnimators

安卓 各种常用功能收藏集

https://www.jianshu.com/p/dad51f6c9c4d

https://www.cnblogs.com/ldq2016/p/5217590.html

Material Design 兼容性控件学习

https://github.com/GitLqr/MaterialDesignDemo

打造千变万化的指示器

https://github.com/JakeWharton/ViewPagerIndicator

https://github.com/astuetz/PagerSlidingTabStrip

https://github.com/hackware1993/MagicIndicator

一个简单的、巧妙的水波纹扩散效果

https://github.com/hackware1993/WaveView

MLSDev-AnimatedRecyclerView - 带有布局动画的RecyclerView

https://www.ctolib.com/MLSDev-AnimatedRecyclerView.html#animatedrecyclerview

全新的快速索引导航栏,联系人字母排序侧边栏

https://github.com/gjiazhe/WaveSideBar

https://github.com/kongnanlive/bubble-scroll

MagicProgressWidget - 渐变的圆形进度条与轻量横向进度条

https://github.com/lingochamp/MagicProgressWidget

安卓炫酷案例汇总

https://blog.csdn.net/lzg13663472636/article/details/78305145

任意拖布局 (扩展自QQ空间的列表Header效果)

https://github.com/Ifxcyr/RandomDragLayout

物流时间轴

https://github.com/XuNeverMore/TimelineDecor

https://github.com/loveAndroidAndroid/TimeLine

https://github.com/Carson-Ho/TimeAxle_View

https://github.com/razerdp/UnderLineLinearLayout

一个漫画阅读项目源码

https://github.com/QiuChenly/iComic

安卓自定义View实现北京地铁线路图

https://github.com/yhongm/android-subway

仿京东app 全新组件化架构升级

https://github.com/liu-xiao-dong/JD-Test?tdsourcetag=s_pcqq_aiomsg

flutter学习案例,接口使用玩Android开放的api,作为入门训练代码案例,耗时大概4个月【业余时间】,已经完成了基本的功能。努力打造一个体验好的flutter版本的玩android客户端!

https://github.com/yangchong211/ycflutter

从A业务组件跳转到业务B组件,并且要携带参数跳转,这时候采用路由ActivityRouter,支持给Activity定义 URL,这样可以通过 URL 跳转到Activity,支持在浏览器以及 app 中跳入

https://github.com/mzule/ActivityRouter

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值