TouTiao开源项目 分析笔记11 以总体到局部的思路 构建图片主列表
1.构建图片主列表的整体片段PhotoTabLayout
1.1.首先创建一个PhotoTabLayout片段
public class PhotoTabLayout extends Fragment { private static final String TAG = "PhotoTabLayout";
private static PhotoTabLayout instance = null;
private static int pageSize = InitApp.AppContext.getResources().getStringArray(R.array.photo_id).length;
private String categoryId[] = InitApp.AppContext.getResources().getStringArray(R.array.photo_id);
private String categoryName[] = InitApp.AppContext.getResources().getStringArray(R.array.photo_name);
private TabLayout tabLayout;
private ViewPager viewPager;
private List<Fragment> fragmentList = new ArrayList<>();
private BasePagerAdapter adapter; public static PhotoTabLayout getInstance() {
if (instance == null) {
instance = new PhotoTabLayout();
}
return instance;
} @Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_photo_tab, container, false);
initView(view);
initData();
return view;
} @Override
public void onResume() {
super.onResume();
tabLayout.setBackgroundColor(SettingUtil.getInstance().getColor());
} private void initView(View view) {
tabLayout = view.findViewById(R.id.tab_layout_photo);
viewPager = view.findViewById(R.id.view_pager_photo); tabLayout.setupWithViewPager(viewPager);
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
tabLayout.setBackgroundColor(SettingUtil.getInstance().getColor());
viewPager.setOffscreenPageLimit(pageSize);
} private void initData() {
for (int i = 0; i < categoryId.length; i++) {
Fragment fragment = PhotoArticleView.newInstance(categoryId[i]);
fragmentList.add(fragment);
}
adapter = new BasePagerAdapter(getChildFragmentManager(), fragmentList, categoryName);
viewPager.setAdapter(adapter);
} public void onDoubleClick() {
if (fragmentList != null && fragmentList.size() > 0) {
int item = viewPager.getCurrentItem();
((BaseListFragment) fragmentList.get(item)).onRefresh();
}
} @Override
public void onDestroy() {
if (instance != null) {
instance = null;
}
super.onDestroy();
}
}
1.2.这个片段需要一个布局fragment_photo_tab.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <android.support.design.widget.TabLayout
android:id="@+id/tab_layout_photo"
style="@style/TabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="@style/AppTheme.AppBarOverlay"
app:tabTextColor="@color/gray">
</android.support.design.widget.TabLayout> <android.support.v4.view.ViewPager
android:id="@+id/view_pager_photo"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:layout="@layout/fragment_list">
</android.support.v4.view.ViewPager> </LinearLayout>
1.3.布局真实效果预览
1.4.然后就是tabLayout的静态文字
在资源文件中values中的photo_category.xml
<?xml version="1.0" encoding="utf-8"?>
<resources> <string-array name="photo_name">
<item>全部</item>
<item>老照片</item>
<item>故事照片</item>
<item>摄影集</item>
</string-array> <string-array name="photo_id">
<item>组图</item>
<item>gallery_old_picture</item>
<item>gallery_story</item>
<item>gallery_photograthy</item>
</string-array>
</resources>
1.5.然后就是将图片主列表的4个小type用一个BasePagerAdapter联系起来
下面的代码在上方的PhotoTabLayout.java中的一个函数中,这里再拿出来讲一下。
private void initData() {
for (int i = 0; i < categoryId.length; i++) {
//这里用了PhotoArticleView,图片中所有fragment都是用这个类,所以要新建这个类了,下面再详细讲解这个类的构造过程
Fragment fragment = PhotoArticleView.newInstance(categoryId[i]);
fragmentList.add(fragment);
}
//这里就是将文字和片段统一的过程了,最重要的就是这个BasePagerAdapter将所有片段集合,设置到viewPager的适配器中
adapter = new BasePagerAdapter(getChildFragmentManager(), fragmentList, categoryName);
viewPager.setAdapter(adapter);
}
1.6.看一下这个片段的适配器吧
名字叫做BasePagerAdapter
public class BasePagerAdapter extends FragmentStatePagerAdapter { private List<Fragment> fragmentList;
private List<String> titleList; public BasePagerAdapter(FragmentManager fm, List<Fragment> fragmentList, String[] titles) {
super(fm);
this.fragmentList = fragmentList;
this.titleList = new ArrayList<>(Arrays.asList(titles));
} public BasePagerAdapter(FragmentManager fm, List<Fragment> fragmentList, List<String> titleList) {
super(fm);
this.fragmentList = fragmentList;
this.titleList = titleList;
} @Override
public Fragment getItem(int position) {
return fragmentList.get(position);
} @Override
public int getCount() {
return titleList.size();
} @Override
public CharSequence getPageTitle(int position) {
return titleList.get(position);
} @Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
} public void recreateItems(List<Fragment> fragmentList, List<String> titleList) {
this.fragmentList = fragmentList;
this.titleList = titleList;
notifyDataSetChanged();
}
}
1.7.第一阶段结束,主要完成了PhotoTabLayout这个图片主页面的布局和相应的逻辑。
第二阶段主要处理4个分片段的布局和逻辑了。
2.图片系列的文章的基础Bean类
2.1.第一集团数据返回==>调用API返回的数据
/**
* has_more : true
* message : success
* next : {"max_behot_time":1494154227}
* data:[[.....]]
*/
2.2.第二集团数据返回==>上面的data返回的数据
//data返回的数据 /**
* image_url : http://p9.pstatp.com/list/640x360/1b820001767b34813c82
* media_avatar_url : http://p1.pstatp.com/large/d2a0011291a5cfef5c3
* article_genre : gallery
* is_diversion_page : false
* title : 我拍少女签约模特王纯粹少女视觉
* middle_mode : false
* gallary_image_count : 41
* image_list : [{"url":"http://p3.pstatp.com/list/640x360/1b820001767b34813c82","width":620,"url_list":[{"url":"http://p9.pstatp.com/list/640x360/1b820001767b34813c82"},{"url":"http://pb1.pstatp.com/list/640x360/1b820001767b34813c82"},{"url":"http://pb3.pstatp.com/list/640x360/1b820001767b34813c82"}],"uri":"list/640x360/1b820001767b34813c82","height":855},{"url":"http://p3.pstatp.com/list/640x360/1b8700015c172e7de742","width":1280,"url_list":[{"url":"http://p3.pstatp.com/list/1b8700015c172e7de742"},{"url":"http://pb9.pstatp.com/list/1b8700015c172e7de742"},{"url":"http://pb1.pstatp.com/list/1b8700015c172e7de742"}],"uri":"list/1b8700015c172e7de742","height":720},{"url":"http://p9.pstatp.com/list/640x360/1b8500015b6a1c897705","width":1656,"url_list":[{"url":"http://p9.pstatp.com/list/1b8500015b6a1c897705"},{"url":"http://pb1.pstatp.com/list/1b8500015b6a1c897705"},{"url":"http://pb3.pstatp.com/list/1b8500015b6a1c897705"}],"uri":"list/1b8500015b6a1c897705","height":2415}]
* more_mode : true
* behot_time : 1494158550
* source_url : /group/6408674663173767426/
* source : 我拍少女
* hot : 1
* is_feed_ad : false
* has_gallery : true
* single_mode : false
* comments_count : 1
* group_id : 6408674663173767426
* media_url : http://toutiao.com/m4439122761/
* honey : true
*/
2.3.整个PhotoArticleBean类
public class PhotoArticleBean { /**
* has_more : true
* message : success
* next : {"max_behot_time":1494154227}
*/ private boolean has_more;
private String message;
private NextBean next;
private List<DataBean> data; public boolean isHas_more() {
return has_more;
} public void setHas_more(boolean has_more) {
this.has_more = has_more;
} public String getMessage() {
return message;
} public void setMessage(String message) {
this.message = message;
} public NextBean getNext() {
return next;
} public void setNext(NextBean next) {
this.next = next;
} public List<DataBean> getData() {
return data;
} public void setData(List<DataBean> data) {
this.data = data;
} public static class NextBean{
private int max_behot_time;
public int getMax_behot_time(){
return max_behot_time;
}
public void setMax_behot_time(int max_behot_tim){
this.max_behot_time=max_behot_time;
}
} public static class DataBean implements Parcelable {
public static final Creator<DataBean> CREATOR = new Creator<DataBean>() {
@Override
public DataBean createFromParcel(Parcel in) {
return new DataBean(in);
} @Override
public DataBean[] newArray(int size) {
return new DataBean[size];
}
};
/**
* image_url : http://p9.pstatp.com/list/640x360/1b820001767b34813c82
* media_avatar_url : http://p1.pstatp.com/large/d2a0011291a5cfef5c3
* article_genre : gallery
* is_diversion_page : false
* title : 我拍少女签约模特王纯粹少女视觉
* middle_mode : false
* gallary_image_count : 41
* image_list : [{"url":"http://p3.pstatp.com/list/640x360/1b820001767b34813c82","width":620,"url_list":[{"url":"http://p9.pstatp.com/list/640x360/1b820001767b34813c82"},{"url":"http://pb1.pstatp.com/list/640x360/1b820001767b34813c82"},{"url":"http://pb3.pstatp.com/list/640x360/1b820001767b34813c82"}],"uri":"list/640x360/1b820001767b34813c82","height":855},{"url":"http://p3.pstatp.com/list/640x360/1b8700015c172e7de742","width":1280,"url_list":[{"url":"http://p3.pstatp.com/list/1b8700015c172e7de742"},{"url":"http://pb9.pstatp.com/list/1b8700015c172e7de742"},{"url":"http://pb1.pstatp.com/list/1b8700015c172e7de742"}],"uri":"list/1b8700015c172e7de742","height":720},{"url":"http://p9.pstatp.com/list/640x360/1b8500015b6a1c897705","width":1656,"url_list":[{"url":"http://p9.pstatp.com/list/1b8500015b6a1c897705"},{"url":"http://pb1.pstatp.com/list/1b8500015b6a1c897705"},{"url":"http://pb3.pstatp.com/list/1b8500015b6a1c897705"}],"uri":"list/1b8500015b6a1c897705","height":2415}]
* more_mode : true
* behot_time : 1494158550
* source_url : /group/6408674663173767426/
* source : 我拍少女
* hot : 1
* is_feed_ad : false
* has_gallery : true
* single_mode : false
* comments_count : 1
* group_id : 6408674663173767426
* media_url : http://toutiao.com/m4439122761/
* honey : true
*/ private String image_url;
private String media_avatar_url;
private String article_genre;
private boolean is_diversion_page;
private String title;
private boolean middle_mode;
private int gallary_image_count;
private boolean more_mode;
private int behot_time;
private String source_url;
private String source;
private int hot;
private boolean is_feed_ad;
private boolean has_gallery;
private boolean single_mode;
private int comments_count;
private String group_id;
private String media_url;
private boolean honey;
private List<ImageListBean> image_list; protected DataBean(Parcel in) {
image_url = in.readString();
media_avatar_url = in.readString();
article_genre = in.readString();
is_diversion_page = in.readByte() != 0;
title = in.readString();
middle_mode = in.readByte() != 0;
gallary_image_count = in.readInt();
more_mode = in.readByte() != 0;
behot_time = in.readInt();
source_url = in.readString();
source = in.readString();
hot = in.readInt();
is_feed_ad = in.readByte() != 0;
has_gallery = in.readByte() != 0;
single_mode = in.readByte() != 0;
comments_count = in.readInt();
group_id = in.readString();
media_url = in.readString();
honey = in.readByte() != 0;
} @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(image_url);
dest.writeString(media_avatar_url);
dest.writeString(article_genre);
dest.writeByte((byte) (is_diversion_page ? 1 : 0));
dest.writeString(title);
dest.writeByte((byte) (middle_mode ? 1 : 0));
dest.writeInt(gallary_image_count);
dest.writeByte((byte) (more_mode ? 1 : 0));
dest.writeInt(behot_time);
dest.writeString(source_url);
dest.writeString(source);
dest.writeInt(hot);
dest.writeByte((byte) (is_feed_ad ? 1 : 0));
dest.writeByte((byte) (has_gallery ? 1 : 0));
dest.writeByte((byte) (single_mode ? 1 : 0));
dest.writeInt(comments_count);
dest.writeString(group_id);
dest.writeString(media_url);
dest.writeByte((byte) (honey ? 1 : 0));
} @Override
public int describeContents() {
return 0;
} public String getImage_url() {
return image_url;
} public void setImage_url(String image_url) {
this.image_url = image_url;
} public String getMedia_avatar_url() {
return media_avatar_url;
} public void setMedia_avatar_url(String media_avatar_url) {
this.media_avatar_url = media_avatar_url;
} public String getArticle_genre() {
return article_genre;
} public void setArticle_genre(String article_genre) {
this.article_genre = article_genre;
} public boolean isIs_diversion_page() {
return is_diversion_page;
} public void setIs_diversion_page(boolean is_diversion_page) {
this.is_diversion_page = is_diversion_page;
} public String getTitle() {
return title;
} public void setTitle(String title) {
this.title = title;
} public boolean isMiddle_mode() {
return middle_mode;
} public void setMiddle_mode(boolean middle_mode) {
this.middle_mode = middle_mode;
} public int getGallary_image_count() {
return gallary_image_count;
} public void setGallary_image_count(int gallary_image_count) {
this.gallary_image_count = gallary_image_count;
} public boolean isMore_mode() {
return more_mode;
} public void setMore_mode(boolean more_mode) {
this.more_mode = more_mode;
} public int getBehot_time() {
return behot_time;
} public void setBehot_time(int behot_time) {
this.behot_time = behot_time;
} public String getSource_url() {
return source_url;
} public void setSource_url(String source_url) {
this.source_url = source_url;
} public String getSource() {
return source;
} public void setSource(String source) {
this.source = source;
} public int getHot() {
return hot;
} public void setHot(int hot) {
this.hot = hot;
} public boolean isIs_feed_ad() {
return is_feed_ad;
} public void setIs_feed_ad(boolean is_feed_ad) {
this.is_feed_ad = is_feed_ad;
} public boolean isHas_gallery() {
return has_gallery;
} public void setHas_gallery(boolean has_gallery) {
this.has_gallery = has_gallery;
} public boolean isSingle_mode() {
return single_mode;
} public void setSingle_mode(boolean single_mode) {
this.single_mode = single_mode;
} public int getComments_count() {
return comments_count;
} public void setComments_count(int comments_count) {
this.comments_count = comments_count;
} public String getGroup_id() {
return group_id;
} public void setGroup_id(String group_id) {
this.group_id = group_id;
} public String getMedia_url() {
return media_url;
} public void setMedia_url(String media_url) {
this.media_url = media_url;
} public boolean isHoney() {
return honey;
} public void setHoney(boolean honey) {
this.honey = honey;
} public List<ImageListBean> getImage_list() {
return image_list;
} public void setImage_list(List<ImageListBean> image_list) {
this.image_list = image_list;
} public static class ImageListBean {
/**
* url : http://p3.pstatp.com/list/640x360/1b820001767b34813c82
* width : 620
* url_list : [{"url":"http://p9.pstatp.com/list/640x360/1b820001767b34813c82"},{"url":"http://pb1.pstatp.com/list/640x360/1b820001767b34813c82"},{"url":"http://pb3.pstatp.com/list/640x360/1b820001767b34813c82"}]
* uri : list/640x360/1b820001767b34813c82
* height : 855
*/ private String url;
private int width;
private String uri;
private int height;
private List<UrlListBean> url_list; public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
} public int getWidth() {
return width;
} public void setWidth(int width) {
this.width = width;
} public String getUri() {
return uri;
} public void setUri(String uri) {
this.uri = uri;
} public int getHeight() {
return height;
} public void setHeight(int height) {
this.height = height;
} public List<UrlListBean> getUrl_list() {
return url_list;
} public void setUrl_list(List<UrlListBean> url_list) {
this.url_list = url_list;
} public static class UrlListBean {
/**
* url : http://p9.pstatp.com/list/640x360/1b820001767b34813c82
*/ private String url; public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url;
}
}
}
}
}
3.图片系列的视图以及处理器控制
3.1.最底层视图以及处理器接口
interface IPhotoArticle { interface View extends IBaseListView<Presenter> { /**
* 请求数据
*/
void onLoadData();
} interface Presenter extends IBasePresenter { /**
* 请求数据
*/
void doLoadData(String... category); /**
* 再起请求数据
*/
void doLoadMoreData(); /**
* 设置适配器
*/
void doSetAdapter(List<PhotoArticleBean.DataBean> dataBeen); void doShowNoMore();
}
}
3.2.图片系列的视图
package com.jasonjan.headnews.module.photo; import android.os.Bundle;
import android.view.View; import com.jasonjan.headnews.adapter.DiffCallback;
import com.jasonjan.headnews.bean.common.LoadingBean;
import com.jasonjan.headnews.main.Register;
import com.jasonjan.headnews.module.base.BaseListFragment;
import com.jasonjan.headnews.util.OnLoadMoreListener; import java.util.List; import me.drakeet.multitype.Items;
import me.drakeet.multitype.MultiTypeAdapter; /**
* Created by JasonJan on 2017/12/13.
*/ public class PhotoArticleView extends BaseListFragment<IPhotoArticle.Presenter> implements IPhotoArticle.View {
private static final String TAG = "PhotoArticleView";
private String categoryId; public static PhotoArticleView newInstance(String categoryId){
Bundle bundle=new Bundle();
bundle.putString(TAG,categoryId);
PhotoArticleView instance=new PhotoArticleView();
instance.setArguments(bundle);
return instance;
} @Override
protected void initData(){
categoryId=getArguments().getString(TAG);
} @Override
protected void initView(View view) {
super.initView(view);
adapter = new MultiTypeAdapter(oldItems);
Register.registerPhotoArticleItem(adapter);
recyclerView.setAdapter(adapter);
recyclerView.addOnScrollListener(new OnLoadMoreListener() {
@Override
public void onLoadMore() {
if (canLoadMore) {
canLoadMore = false;
presenter.doLoadMoreData();
}
}
});
} @Override
public void onLoadData() {
onShowLoading();
presenter.doLoadData(categoryId);
} @Override
public void onSetAdapter(final List<?> list) {
Items newItems = new Items(list);
newItems.add(new LoadingBean());
DiffCallback.notifyDataSetChanged(oldItems, newItems, DiffCallback.PHOTO, adapter);
oldItems.clear();
oldItems.addAll(newItems);
canLoadMore = true;
} @Override
public void setPresenter(IPhotoArticle.Presenter presenter) {
if (null == presenter) {
this.presenter = new PhotoArticlePresenter(this);
}
} }
然后发现处理器还没有设置呢。
然后还有视图绑定也未实现(在自定义的Register中实现)
然后还有处理新老数据来刷新(在自定义的Diffback中实现)
3.3.图片系列的处理器
package com.jasonjan.headnews.module.photo; import com.jasonjan.headnews.bean.photo.PhotoArticleBean;
import com.jasonjan.headnews.main.ErrorAction;
import com.jasonjan.headnews.main.RetrofitFactory;
import com.jasonjan.headnews.util.TimeUtil; import java.util.ArrayList;
import java.util.List; import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
import io.reactivex.schedulers.Schedulers; /**
* Created by JasonJan on 2017/12/13.
*/ public class PhotoArticlePresenter implements IPhotoArticle.Presenter { private static final String TAG="PhotoArticlePresenter";
private IPhotoArticle.View view;
private List<PhotoArticleBean.DataBean> dataList=new ArrayList<>();
private String category;
private String time; PhotoArticlePresenter(IPhotoArticle.View view) {
this.view = view;
this.time = TimeUtil.getCurrentTimeStamp();
} @Override
public void doLoadData(String... category){
try {
if (null == this.category) {
this.category = category[0];
}
} catch (Exception e) {
ErrorAction.print(e);
} // 释放内存
if (dataList.size() > 100) {
dataList.clear();
} RetrofitFactory.getRetrofit().create(IPhotoApi.class).getPhotoArticle(this.category, time)
.subscribeOn(Schedulers.io())
.switchMap(new Function<PhotoArticleBean, Observable<PhotoArticleBean.DataBean>>() {
@Override
public Observable<PhotoArticleBean.DataBean> apply(@NonNull PhotoArticleBean photoArticleBean) throws Exception {
List<PhotoArticleBean.DataBean> data = photoArticleBean.getData();
// 移除最后一项 数据有重复
if (data.size() > 0)
data.remove(data.size() - 1);
//time = photoArticleBean.getNext().getMax_behot_time()+"";
return Observable.fromIterable(data);
}
})
.filter(new Predicate<PhotoArticleBean.DataBean>() {
@Override
public boolean test(@NonNull PhotoArticleBean.DataBean dataBean) throws Exception {
// 去除重复新闻
for (PhotoArticleBean.DataBean bean : dataList) {
if (dataBean.getTitle().equals(bean.getTitle())) {
return false;
}
}
return true;
}
})
.toList()
.compose(view.<List<PhotoArticleBean.DataBean>>bindToLife())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<PhotoArticleBean.DataBean>>() {
@Override
public void accept(@NonNull List<PhotoArticleBean.DataBean> list) throws Exception {
if (null != list && list.size() > 0) {
doSetAdapter(list);
} else {
doShowNoMore();
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
doShowNetError();
ErrorAction.print(throwable);
}
});
} @Override
public void doLoadMoreData(){
doLoadData();
} @Override
public void doSetAdapter(List<PhotoArticleBean.DataBean> dataBean){
dataList.addAll(dataBean);
view.onSetAdapter(dataList);
view.onHideLoading();
} @Override
public void doRefresh() {
if (dataList.size() != 0) {
dataList.clear();
time = TimeUtil.getCurrentTimeStamp()+"";
}
view.onShowLoading();
doLoadData();
} @Override
public void doShowNetError() {
view.onHideLoading();
view.onShowNetError();
} @Override
public void doShowNoMore() {
view.onHideLoading();
view.onShowNoMore();
}
}
3.4.图片系列文章的API接口请求
public interface IPhotoApi { /**
* 获取图片标题等信息
* http://www.toutiao.com/api/article/feed/?category=类型&as=A115C8457F69B85&cp=585F294B8845EE1&_=时间&count=30
*/
@GET("http://www.toutiao.com/api/pc/feed/?as=A1C598BB87BE7DA&cp=58B72ED7AD3A0E1")
Observable<PhotoArticleBean> getPhotoArticle(
@Query("category") String category,
@Query("max_behot_time") String maxBehotTime); /**
* 获取图片内容HTML内容
* 抓取 url 较复杂
* 详情查看 {@linkplain com.meiji.toutiao.module.photo.content.PhotoContentPresenter#doLoadData(String...)}
*/
@GET()
@Headers("User-Agent:" + Constant.USER_AGENT_PC)
Call<ResponseBody> getPhotoContentHTML(@Url String url);
}
3.5.然后是Register类中声明3中Binder
public static void registerPhotoArticleItem(@NonNull MultiTypeAdapter adapter) {
adapter.register(PhotoArticleBean.DataBean.class, new PhotoArticleViewBinder());
adapter.register(LoadingBean.class, new LoadingViewBinder());
adapter.register(LoadingEndBean.class, new LoadingEndViewBinder());
}
3.6.然后在DiffCallback中加这个类型吧
在areItemsTheSame函数中添加Photo类型。
case PHOTO:
return ((PhotoArticleBean.DataBean) oldList.get(oldItemPosition)).getTitle().equals(
((PhotoArticleBean.DataBean) newList.get(newItemPosition)).getTitle());
在areContentsTheSame中添加Photo类型
case PHOTO:
return ((PhotoArticleBean.DataBean) oldList.get(oldItemPosition)).getSource_url().equals(
((PhotoArticleBean.DataBean) newList.get(newItemPosition)).getSource_url());
4.视图绑定类PhotoArticleViewBinder
4.1.视图绑定类的布局定义
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginTop="4dp"
android:background="@color/viewBackground"
app:cardElevation="1dp"> <RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:foreground="?attr/selectableItemBackground"
android:padding="16dp"> <LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"> <com.meiji.toutiao.widget.CircleImageView
android:id="@+id/iv_media"
android:layout_width="22dp"
android:layout_height="22dp"
android:scaleType="centerCrop"/> <TextView
android:id="@+id/tv_extra"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLength="30"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:text="新闻源 - 1小时前"/> <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"> <ImageView
android:id="@+id/iv_dots"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:padding="4dp"
android:scaleType="center"
app:srcCompat="@drawable/ic_dots_horizontal_grey500_24dp"
tools:ignore="ContentDescription"/>
</RelativeLayout> </LinearLayout> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/header"
android:layout_marginTop="4dp"
android:orientation="vertical"> <TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:textStyle="bold"
tools:text="看完王健林的私人飞机,再看刘强东的,刚好相差1个小目标的钱"/> <LinearLayout
android:layout_width="match_parent"
android:layout_height="82dp"
android:layout_marginTop="4dp"
android:orientation="horizontal"> <ImageView
android:id="@+id/iv_0"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:layout_weight="1"
android:src="@color/viewBackground"
tools:ignore="ContentDescription"/> <ImageView
android:id="@+id/iv_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:layout_weight="1"
android:src="@color/viewBackground"
tools:ignore="ContentDescription"/> <ImageView
android:id="@+id/iv_2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:src="@color/viewBackground"
tools:ignore="ContentDescription"/> </LinearLayout>
</LinearLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
4.2.视图效果
4.3.视图绑定类
public class PhotoArticleViewBinder extends ItemViewBinder<PhotoArticleBean.DataBean, PhotoArticleViewBinder.ViewHolder> { @NonNull
@Override
protected PhotoArticleViewBinder.ViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
View view = inflater.inflate(R.layout.item_photo_article, parent, false);
return new ViewHolder(view);
} @Override
protected void onBindViewHolder(@NonNull final ViewHolder holder, @NonNull final PhotoArticleBean.DataBean item) { final Context context = holder.itemView.getContext(); try {
String tv_title = item.getTitle();
if (!TextUtils.isEmpty(item.getMedia_avatar_url())) {
ImageLoader.loadCenterCrop(context, item.getMedia_avatar_url().replaceFirst("//","http://").trim(), holder.iv_media, R.color.viewBackground,R.mipmap.error_image);
} if (item.getImage_list() != null) {
int size = item.getImage_list().size();
String[] ivs = new String[size];
for (int i = 0; i < item.getImage_list().size(); i++) {
ivs[i] = item.getImage_list().get(i).getUrl().replaceFirst("//","http://").trim();
}
switch (ivs.length) {
case 1:
ImageLoader.loadCenterCrop(context, ivs[0], holder.iv_0, R.color.viewBackground,R.mipmap.error_image);
break;
case 2:
ImageLoader.loadCenterCrop(context, ivs[0], holder.iv_0, R.color.viewBackground,R.mipmap.error_image);
ImageLoader.loadCenterCrop(context, ivs[1], holder.iv_1, R.color.viewBackground,R.mipmap.error_image);
break;
case 3:
ImageLoader.loadCenterCrop(context, ivs[0], holder.iv_0, R.color.viewBackground,R.mipmap.error_image);
ImageLoader.loadCenterCrop(context, ivs[1], holder.iv_1, R.color.viewBackground,R.mipmap.error_image);
ImageLoader.loadCenterCrop(context, ivs[2], holder.iv_2, R.color.viewBackground,R.mipmap.error_image);
break;
}
Log.i("图片绑定类:","\n头像地址="+item.getMedia_avatar_url().replaceFirst("//","")+"\n图片链接="+ivs[0]+"\n"+ivs[1]+"\n"+ivs[2]); } String tv_source = item.getSource();
String tv_datetime = item.getBehot_time() + "";
String comments_count = item.getComments_count() + "评论";
if (!TextUtils.isEmpty(tv_datetime)) {
tv_datetime = TimeUtil.getTimeStampAgo(tv_datetime);
}
holder.tv_title.setText(tv_title);
holder.tv_title.setTextSize(SettingUtil.getInstance().getTextSize());
holder.tv_extra.setText(tv_source + " - " + comments_count + " - " + tv_datetime);
holder.iv_dots.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
PopupMenu popupMenu = new PopupMenu(context,
holder.iv_dots, Gravity.END, 0, R.style.MyPopupMenu);
popupMenu.inflate(R.menu.menu_share);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menu) {
int itemId = menu.getItemId();
if (itemId == R.id.action_share) {
String shareUrl = item.getSource_url();
if (!shareUrl.contains("toutiao")) {
shareUrl = "http://toutiao.com" + shareUrl;
}
IntentAction.send(context, item.getTitle() + "\n" + shareUrl);
}
return false;
}
});
popupMenu.show();
}
}); RxView.clicks(holder.itemView)
.throttleFirst(1, TimeUnit.SECONDS)
.subscribe(new Consumer<Object>() {
@Override
public void accept(@io.reactivex.annotations.NonNull Object o) throws Exception {
PhotoContentActivity.launch(item);
}
});
} catch (Exception e) {
ErrorAction.print(e);
}
} public class ViewHolder extends RecyclerView.ViewHolder { private CircleImageView iv_media;
private TextView tv_extra;
private TextView tv_title;
private ImageView iv_0;
private ImageView iv_1;
private ImageView iv_2;
private ImageView iv_dots; public ViewHolder(View itemView) {
super(itemView);
this.iv_media = itemView.findViewById(R.id.iv_media);
this.tv_extra = itemView.findViewById(R.id.tv_extra);
this.tv_title = itemView.findViewById(R.id.tv_title);
this.iv_0 = itemView.findViewById(R.id.iv_0);
this.iv_1 = itemView.findViewById(R.id.iv_1);
this.iv_2 = itemView.findViewById(R.id.iv_2);
this.iv_dots = itemView.findViewById(R.id.iv_dots);
}
}
}
5.目前效果预览
5.1.目前完成的工作
新闻的三种大类型
图片的一种大类型,这种大类型有4个分类,为全部、老照片、故事照片和摄影集。
但是每种类型的评论以及详细页面还未实现。
因为图片调用今日头条的API不稳定,有时候可以获得数据,有时候无法获得。
所以这里是无法确保每次刷新都有新的数据。
5.2.手机真实数据预览
TouTiao开源项目 分析笔记11 以总体到局部的思路 构建图片主列表的更多相关文章
- TouTiao开源项目 分析笔记12 从总体到局部 构建视频主页面
1.构建视频主列表的整体碎片VideoTabLayout 1.1.首先创建一个VideoTabLayout package com.jasonjan.headnews.module.video; im ...
- TouTiao开源项目 分析笔记2
1.Constant常量定义类 1.1.源代码 public class Constant { public static final String USER_AGENT_MOBILE = " ...
- TouTiao开源项目 分析笔记9 实现一个问答主页面
1.根据API返回创建几个基础的Bean 1.1.WendaArticleDataBean类 API返回的数据如下: /** * cell_type : 36 * extra : {"wen ...
- TouTiao开源项目 分析笔记6
1.NewsChannelBean简单类笔记 1.1.Comparable接口的实现和使用 参考文章:Comparable接口的实现和使用. 因为NewsChannelBean实现了Comparabl ...
- TouTiao开源项目 分析笔记4==>一个简单APP 整体常用框架
1.效果预览 1.1.如下图所以,到目前为止所有的功能. 2.从InitApp开始->SplashActivity->MainActivity 2.1.InitApp源代码.这是整个项目的 ...
- TouTiao开源项目 分析笔记15 新闻详情之两种类型的实现
1.预览效果 1.1.首先看一下需要实现的效果. 第一种,文字类型新闻. 第二种,图片类型新闻. 1.2.在NewsArticleTextViewBinder中设置了点击事件 RxView.click ...
- TouTiao开源项目 分析笔记10 实现通用普通文章片段页面
1.RxJava的Observable数据操作符总结 1.1.Map操作符 Map操作符对原始Observable发射的没一项数据应用一个你选择的函数, 然后返回一个发射这些结果的Observable ...
- TouTiao开源项目 分析笔记18 视频详情页面
1.效果预览 1.1.需要做到的真实效果 1.2.触发的点击事件 在MediaArticleVideoViewBinder的每一个item点击事件中: VideoContentActivity.lau ...
- TouTiao开源项目 分析笔记17 新闻媒体专栏
1.效果预览 1.1.要实现的效果 1.2.如何调转到新闻媒体专栏 点击右上角的用户图标. 在新闻详情页面的Fragment的菜单点击事件中触发. case R.id.action_open_medi ...
随机推荐
- HCNA配置console线路密码aaa认证
Please check whether system data has been changed, and save data in time Configuration console time ...
- COGS 146. [USACO Jan08] 贝茜的晨练计划
★☆ 输入文件:cowrun.in 输出文件:cowrun.out 简单对比时间限制:1 s 内存限制:32 MB 奶牛们打算通过锻炼来培养自己的运动细胞,作为其中的一员,贝茜选择的运 ...
- Objectbox Box的getAll() 函数返回emptylist() 未判断导致崩溃
最近使用了Objectbox作为新项目的数据库后台,Greendao开发团队新力作,但是Objectbox算是比较新的一个东西,现在资料也不多. 今天跟大家分享一个关于Box类的getAll()函数的 ...
- MySQL入门很简单: 3 操作数据库
登陆:mysq -u root -p 0409 1). 创建, 删除数据库 SHOW DATABASES; 显示已经存在的数据率 CREATE DATABASES 数据库名: 创建数据库 DROP D ...
- 504. Inverted Index (Map Reduce) lintcode
https://www.lintcode.com/problem/inverted-index-map-reduce/description -- decription of the map redu ...
- 将Apache2.4手动安装成Windows的服务
将Apache2.4手动安装成Windows的服务 可以选择在安装Apache时自动将其安装为一个服务.如果选择"for all users",那么Apache将会被安装为服务. ...
- 前端高质量知识(四)-JS详细图解作用域链与闭包
攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你是初入前端的朋友,我没有办法 ...
- 谷歌Web中文开发手冊:3响应式
https://developers.google.com/web/fundamentals/getting-started/your-first-multi-screen-site/responsi ...
- 转:SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
转:https://www.cnblogs.com/zyw-205520/p/4771253.html 1.基本概念 1.1.Spring Spring是一个开源框架,Spring是于2003 年 ...
- maven没有servlet(创建servlet后报错)
maven不能创建servlet 解决方案 方案一 在项目的iml进行指定根目录 <sourceRoots> <root url="file://$MODULE_DIR$/ ...