用react模仿今日头条

2019-06-13 23:42:08 Amen_Wu 阅读数 298

学习REACT+RN+WEUI
在这里插入图片描述

  1. 安装自动按需加载样式插件: npm install babel-plugin-import --save-dev
    {
    “plugins”: [
    [“import”, { libraryName: “antd-mobile”, style: “css” }] // style: true 会加载 less 文件
    ]
    }
    // babel-plugin-import 会帮助你加载 JS 和 CSS
    import { DatePicker } from ‘antd-mobile’;
  2. 手动引入
    import DatePicker from ‘antd-mobile/lib/date-picker’; // 加载 JS
    import ‘antd-mobile/lib/date-picker/style/css’; // 加载 CSS
    // import ‘antd-mobile/lib/date-picker/style’;

在这里插入图片描述

2017-12-13 11:23:24 qq_33563147 阅读数 4515
<div class="topmenu border-bottom-cd">
  <div class="swiper-container">
    <div class="swiper-wrapper">
      <span class="swiper-slide padding" ng-click="selectedTab($index)" ng-repeat="slide in slides" id="{{slide.id}}" style="width:auto" ng-class="{true:'font-weight-bold color-f23c39 ',false:''}[slideIndex ==$index]">{{slide.name}}</span>
      <span class="swiper-slide padding width-percentage-10" ></span>
      <span class="swiper-slide padding width-percentage-10"></span>
      <span class="swiper-slide padding width-percentage-10"></span>
      <span class="swiper-slide padding width-percentage-10"></span>
    </div>
  </div>
</div>
<ion-slide-box show-pager="false" class="has-header" on-slide-changed="slideChanged($index)">
  <ion-slide ng-repeat="slide in slides"  >
    <div class="list">
      <h2>title:{{slide.name}}</h2>
    </div>
  </ion-slide>
</ion-slide-box>


$scope.slides = [
  {name: '健康资讯',
  id:1},
  {name: '健康知识',
  id:2},
  {name: '健康问答',
  id:3},
  {name: '健康图书',
  id:4},
  {name: '健康资讯',
  id:5},
  {name: '健知识',
  id:6},
  {name: '健问答',
  id:7},
  {name: '健图书',
  id:8},
  {name: '图书',
    id:9},
  {name: '资讯',
  id:10},
  {name: '知识',
  id:11},
  {name: '问答',
  id:12},
  {name: '图书',
  id:13},
  {name: '健康资讯',
  id:14},
  {name: '健康知识',
  id:15},
  {name: '健康问答',
    id:16},
  {name: '健康图书',
  id:17},
  {name: '健康资讯',
    id:18},
];
$scope.slideIndex = 0;

//初始化swiper
var swiper = new Swiper('.swiper-container', {
  freeMode : true,
});

$scope.slideChanged = function (index) {
  $scope.slideIndex=index;
  //定位
  $location.hash( $scope.slides[index].id);
  $anchorScroll();
};
$scope.selectedTab = function (index) {
  $scope.slideIndex=index;
  //滑动索引,跳转到指定地方
  $ionicSlideBoxDelegate.slide(index)
}

使用http://www.swiper.com.cn/    
swiper.js 插件安装   npm install swiper  
2017-05-19 17:04:43 sinat_17775997 阅读数 4228

技术栈:

https://github.com/Topthinking/react-webapp-dianping

react + react-router4.1 + redux + less + ES6/7 + webpack + fetch + bundle-loader

下载

git clone https://github.com/Topthinking/react-webapp-dianping.git

cd react-webapp-dianping

npm install

运行(nodejs 6.0+)

 npm run dev (正常编译模式)

 访问 http://localhost:8080

 npm run build (发布生产版本,对代码进行混淆压缩,提取公共代码,分离css文件)

2017年5月8日

初次提交

2017年5月14日

1.利用webpack的bundle-loader实现代码分割,最终实现根据路由按需加载,加快用户对首屏的访问速度
2.添加fastclick解决移动端300毫秒延迟
3.实现详情页面,复用之前的列表组件和加载组件,只是样式和数据结构不一样

说明

本项目主要理解 react 和 redux 的原理,以及 react + redux 之间的配合方式,同时对react-router4进行由浅入深的学习和探究,欢迎大家一起学习新的路由方式

如果觉得不错的话,您可以点右上角 "Star" 支持一下 谢谢! ^_^

如有问题请直接在 Issues 中提,或者您发现问题并有非常好的解决方案,欢迎 PR :+1:

我的个站首页使用的是angular1.4构建的。地址在这里

演示

demo(请用chrome的手机模式预览)

2018-06-07 14:38:31 shenggaofei 阅读数 2141

很多博客上都有类似今日头条的一些案例,频道管理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

2016-07-31 18:15:12 jj120522 阅读数 7219

      今日头条应用想必大家应该都不陌生,绝对称得上今日之星。媒体上面已经所向披靡,PK掉微信公众号是迟早的事情,我祝今日头条再创辉煌!

      首先我们先看下今日头条的首页:

      

      这里提到一个开源框架:react-native-scrollable-tab-view,类似于安卓中的ViewPagerIndicator+ViewPager等,下面我简单介绍下如何应用以及如何来拓展实现我们想要的效果.

      首先我们先把框架拉进来:npm install react-native-scrollable-tab-view --save 虽说安卓现在用gradle来集成第三方框架方便的许多,但感觉还是react相对简单方便。然后我们就可以引用框架的相关元素。这里用到框架内部的ScrollableTabView,ScrollableTabBar两个组件.其实框架内部还有其他的组件:DefaultTabBar,FacebookTabBar等.

<ScrollableTabView
      initialPage={0}
      scrollWithoutAnimation={true}
      renderTabBar={()=><ScrollableTabBar
                    underlineColor='#ce3d3a'
                    activeTextColor='#fff'
                    inactiveTextColor='rgba(255, 255, 255, 0.7)'
                    underlineHeight={0}
                    textStyle={{ fontSize: 15 }}
                    tabStyle={{ paddingBottom: 0 }}
                    backgroundColor='#ce3d3a'
                    tabStyle={{paddingLeft:12,paddingRight:12}}
                   />}
      >
     <View tabLabel='推荐' style={styles.itemLayout}><Text >推荐板块</Text></View>
     <View tabLabel='头条号'  style={styles.itemLayout}><Text>头条号板块</Text></View>
     <View tabLabel='热点' style={styles.itemLayout}><Text >热点板块</Text></View>
     <View tabLabel='视频'  style={styles.itemLayout}><Text >视频板块</Text></View>
     <View tabLabel='上海'  style={styles.itemLayout}><Text >上海板块</Text></View>
     <View tabLabel='社会'  style={styles.itemLayout}><Text >社会板块</Text></View>
     <View tabLabel='图片'  style={styles.itemLayout}><Text >图片板块</Text></View>
     <View tabLabel='娱乐'  style={styles.itemLayout}><Text >娱乐板块</Text></View>
     <View tabLabel='科技'  style={styles.itemLayout}><Text >科技板块</Text></View>
     <View tabLabel='汽车'  style={styles.itemLayout}><Text >汽车板块</Text></View>
     </ScrollableTabView>

 至此,我们就简单了实现了整个框架,但是右上角的添加搜索按钮如何加上去呢,这就使得我们不得不拓展第三方的框架,首先我们先了解下加入的框架内部结构:


我们要做的就是在ScrollableTabBar.js中的render方法相应的位置加入我们的自定义的按钮组件.

下面我们就自定义添加和搜索两个按钮的TitleButton组件.相对比较简单.

render(){
     return(
       <View style={[styles.titleBar,this.props.style]}>
        <TouchableOpacity style={{flex:1}} onPress={()=>alert('add')}>
        <Image resizeMode='contain' style={styles.search} source={require('../images/add_channel_titlbar.png')}/>
        </TouchableOpacity>
        <TouchableOpacity style={{flex:1}} onPress={()=>alert('search')}>
        <Image style={[styles.search,{width:30,height:30,}]} source={require('../images/white_search_titlebar.png')}/>
        </TouchableOpacity>
       </View>
     );
   }

接下来我们打开第三方框架中ScrollableTabBar,将我们的TitleButton组件加入进去。注意布局是position,其实就达到覆盖在原有的组件上面。

return  <View
      style={[styles.container, {backgroundColor: this.props.backgroundColor, }, this.props.style]}
      onLayout={this.onContainerLayout}
    >
      <ScrollView
        ref={(scrollView) => { this._scrollView = scrollView; }}
        horizontal={true}
        showsHorizontalScrollIndicator={false}
        showsVerticalScrollIndicator={false}
        directionalLockEnabled={true}
        bounces={false}
      >
        <View
          style={[styles.tabs, {width: this.state._containerWidth, }, this.props.tabsContainerStyle]}
          ref={'tabContainer'}
          onLayout={this.onTabContainerLayout}
        >
          {this.props.tabs.map((tab, i) => this.renderTabOption(tab, i))}
          <Animated.View style={[tabUnderlineStyle, dynamicTabUnderline, ]} />
        </View>
      </ScrollView>
     <TitleButton style={{height:49,position:'absolute',top:0,right:0}}/>
    </View>;
到这里我们就完成了整个模拟的过程.

效果图:

         

      Tips:在安卓手机上操作有卡顿现象,但在ios上面操作挺流畅的,不过整体来说还是不错的,希望Fackbook尽快完善React,使其的性能尽可能的流畅!!!


   源码链接


 RN开发群:527459711.欢迎大伙加入.








    

React和Vue对比

阅读数 214