一起撸一波干货集中营练练手Android(三)布局+实现篇

MPGankIO 布局篇

整个App到了这里就开始准备开始实现逻辑啦,有没有点小期待
后续如果有需要可以爬一波包包通缉令的数据O(∩_∩)O~~

我们的布局采用5.0之后的新布局控件

1.CardView

CardView 对这个控件还不太了解的可以戳这里

CardView特别的属性如下:

android:cardCornerRadius:在布局中设置card圆角的大小
CardView.setRadius:在代码中设置card圆角的大小
android:cardBackgroundColor:在布局中设置card背景的颜色
android:elevation:设置阴影的大小
card_view:cardElevation 阴影的大小
card_view:cardMaxElevation 阴影最大高度
card_view:cardCornerRadius 卡片的圆角大小
card_view:contentPadding 卡片内容于边距的间隔 
card_view:contentPaddingBottom 
card_view:contentPaddingTop 
card_view:contentPaddingLeft 
card_view:contentPaddingRight 
card_view:contentPaddingStart 
card_view:contentPaddingEnd
card_view:cardUseCompatPadding 设置内边距,V21+的版本和之前的版本仍旧具有一样的计算方式
**card_view:cardPreventConrerOverlap 
在V20和之前的版本中添加内边距,这个属性为了防止内容和边角的重叠**

  • 我们要使用这个CardView来做卡片式的列表
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
                                    xmlns:app="http://schemas.android.com/apk/res-auto"
                                    android:layout_width="match_parent"
                                    android:layout_height="wrap_content"
                                    android:layout_margin="5dp"
                                    android:foreground="?android:attr/selectableItemBackground"
                                    android:orientation="vertical"
                                    app:contentPadding="5dp">

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

        <TextView
            android:id="@id/tv_title"
            style="@style/txt_normal_style"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:ellipsize="end"
            android:gravity="start"
            android:maxLines="3"
            android:text="标题"
            android:textSize="16sp"
            />


        <TextView
            android:id="@id/tv_name"
            style="@style/txt_normal_style"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_below="@id/tv_title"
            android:layout_marginTop="10dp"
            android:gravity="bottom"
            android:text="作者"
            android:textColor="@color/colorPrimary"/>

        <TextView
            android:id="@id/tv_publishtime"
            style="@style/txt_normal_style"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_below="@id/tv_title"
            android:layout_marginTop="10dp"
            android:gravity="bottom"
            android:text="发表时间"/>

    </RelativeLayout>

</android.support.v7.widget.CardView>

CardView 大部分适用于 listView 或者recyclerView的item中。

2.RecyclerView

RecyclerView 可以戳这里了解一下

RecyclerView与ListView原理是类似的:都是仅仅维护少量的View并且可以展示大量的数据集。RecyclerView用以下两种方式简化了数据的展示和处理:

  • 使用LayoutManager来确定每一个item的排列方式。
  • 为增加和删除项目提供默认的动画效果。

你也可以定义你自己的LayoutManager和添加删除动画,SDK中默认包含了三种布局

 1. 横向布局(LinearLayoutManager)  
 2. Grid布局(GridLayoutManager) 
 3.  瀑布流布局(StaggeredGridLayoutManager)
  • Adapter:使用RecyclerView之前,你需要一个继承自RecyclerView.Adapter的适配器,作用是将数据与每一个item的界面进行绑定。
  • LayoutManager:用来确定每一个item如何进行排列摆放,何时展示和隐藏。回收或重用一个View的时候,
    LayoutManager会向适配器请求新的数据来替换旧的数据,这种机制避免了创建过多的View和频繁的调用findViewById方法(与ListView原理类似)。

总而言之就相当于ListView封装后再抽象,使item更加灵活

 项目中我使用的是XRecyclerView,是封装了上拉刷新和下拉加载的一个库,非常好用还有挺多的加载动画可以切换,使用的方法也很简单,哈哈,偷懒!! 不都是说为了不要重复造轮子嘛~你懂的

3.Fragment

这个应该不用多讲了相信大家都会的

4.代码附上

MainFragment.java 这个就是为了在主页将一些数据初始化为了给TypeFragment拿到类型数据

  • 为什么使用FragmentStateAdapter而不用FragmentPagerAdapter是因为viewpager页数比较多
    FragmentStateAdatper的机制是空置的时候将其销毁释放内存,而FragmentPagerAdapter是将其dettach
    因此选用FragmentSteteAdapter

GankViewPagerAdapter.java

public class GankViewPagerAdapter extends FragmentStatePagerAdapter {
   
    private List<Fragment> mFragments;
    private List<String> mTitles;

    public GankViewPagerAdapter(FragmentManager fm, List<Fragment> fragments, List<String> titles) {
        super(fm);
        this.mFragments = fragments;
        this.mTitles = titles;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        return mTitles.get(position);
    }
}

大家如果之前理解过MVP架构应该就会发现我调用的方式啦!

TypeFragment.java 核心模块


public class TypeFragment extends BaseMvpFragment<TypeFragmentView, TypePresenter> implements
        TypeFragmentView {
    private static final String ARG_PARAM1 = "type";
    @BindView(R.id.rv_list)
    XRecyclerView rvList;
    private String mCategory;
    private View rootView;
    private GankTypeCardAdapter mGankTypeCardAdapter;
    private List<GankDataEntity> mDatas;
    private int curPage = 1;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mCategory = getArguments().getString(ARG_PARAM1);
        }
        initVariables();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_type, container, false);
        ButterKnife.bind(this, rootView);
        resetDatas();
        initViews();
        return rootView;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(ARG_PARAM1, mCategory);
    }

    @Override
    public void onViewStateRestored(Bundle savedInstanceState) {
        super.onViewStateRestored(savedInstanceState);
        if (savedInstanceState != null) {
            mCategory = ConvertUtil.toString(savedInstanceState.getString(ARG_PARAM1), "");//避免为NULL
        }
    }

    @Override
    public void initVariables() {
        mDatas = new ArrayList<>();
    }

    @Override
    public void initViews() {
        initRecyclerView();
        presenter.getTypeGankList();
    }

    private void initRecyclerView() {
        rvList.setPullRefreshEnabled(true);
        rvList.setLoadingMoreEnabled(true);
        rvList.setLoadingListener(new XRecyclerView.LoadingListener() {
            @Override
            public void onRefresh() {
                resetDatas();
                presenter.getTypeGankList();
            }

            @Override
            public void onLoadMore() {
                presenter.getTypeGankList();
            }
        });
    }

    @Override
    protected TypePresenter initPresenter() {
        return new TypePresenter();
    }

    public static TypeFragment newInstance(String type) {
        TypeFragment fragment = new TypeFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, type);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public String getCategory() {
        return mCategory;
    }

    @Override
    public int getCurPage() {
        return curPage;
    }

    @Override
    public void addOnePage() {
        curPage++;
    }

    @Override
    public void makeUpDatas(List<GankDataEntity> datas) {
        mDatas.addAll(datas);
    }

    @Override
    public void notifyAdapter() {
        if (mGankTypeCardAdapter == null) {
            mGankTypeCardAdapter = new GankTypeCardAdapter(mActivity, mDatas);
            rvList.setLayoutManager(new LinearLayoutManager(mActivity));
            rvList.setAdapter(mGankTypeCardAdapter);
        }
        // TODO: 16/8/19 //此处可以优化成动画效果以及item逐个优化不需要整个重新排列
        mGankTypeCardAdapter.notifyDataSetChanged();
        rvList.refreshComplete();
        rvList.loadMoreComplete();
    }

    @Override
    public void resetDatas() {
        curPage = 1;
        mDatas.clear();
        if (mGankTypeCardAdapter != null) {
            mGankTypeCardAdapter.notifyDataSetChanged();
        }
    }

    @Override
    public void onDestroyView() {
        mGankTypeCardAdapter = null;
        super.onDestroyView();
    }
}

最后就是P中调用V啦 就是传说中的解耦


到此基本上的请求列表功能结束啦,下一篇最终章就是列表点击事件和链接的内容页面!

有问题可以来我博客或者简书反馈,谢谢大家的停留在这里时间

附上源码下载地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容