1.NewsChannelBean简单类笔记

1.1.Comparable接口的实现和使用

  参考文章:Comparable接口的实现和使用。

  因为NewsChannelBean实现了Comparable<NewsChannelBean>

public class NewsChannelBean implements Comparable<NewsChannelBean>

  一开始不明白为什么要实现这个接口。

  ==>其实就是强行对实现它的每个类的对象进行整体排序。

  

  之后会报错,因为没有重写比较函数。

  再重写一个这个函数进行大小比较

 @Override
public int compareTo(@NonNull NewsChannelBean o) {
return this.position - o.getPosition();
}

  这个bean类有一个position。所以间接利用position进行了大小的排序。写得非常妙。

  

1.2.重写一个equals函数判断两个对象是否相等 

 @Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
NewsChannelBean bean = (NewsChannelBean) o;
if (isEnable != bean.isEnable)
return false;
if (position != bean.position)
return false;
if (channelId != null ?
          !channelId.equals(bean.channelId) : bean.channelId != null)
return false;
return channelName != null ?
          channelName.equals(bean.channelName) : bean.channelName == null;
}

  整个比较过程就是按照顺序一个一个比较,直到全部都相同为止。

1.3.重写equals的时候,必须重写hashCode函数。

  参考文章:浅谈Java中的hashcode方法。 

下面这段话摘自Effective Java一书:

在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终
如一地返回同一个整数。
如果两个对象根据equals方法比较是相等的,那么调用两个对象的hashCode方法必须返回相同的整数结果。
如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。
  对于第二条和第三条很好理解,但是第一条,很多时候就会忽略。在《Java编程思想》一书中的P495页也有同第一条类似的
一段话:   “设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。
如果在讲一个对象用put()添加进HashMap时产生一个hashCdoe值,而用get()取出时却产生了另一个hashCode值,
那么就无法获取该对象了。所以如果你的hashCode方法依赖于对象中易变的数据,用户就要当心了,
因为此数据发生变化时,hashCode()方法就会生成一个不同的散列码”。

  这里重写返回哈希的函数为:

 @Override
public int hashCode() {
int result = channelId != null ? channelId.hashCode() : 0;
result = 31 * result + (channelName != null ? channelName.hashCode() : 0);
result = 31 * result + isEnable;
result = 31 * result + position;
return result;
}

1.4.所以整个bean类就搞定了。

package com.meiji.toutiao.bean.news;

import android.support.annotation.NonNull;

/**
* Created by Meiji on 2017/3/10.
*/ public class NewsChannelBean implements Comparable<NewsChannelBean> { private String channelId;
private String channelName;
private int isEnable;
private int position; public String getChannelId() {
return channelId;
} public void setChannelId(String channelId) {
this.channelId = channelId;
} public String getChannelName() {
return channelName;
} public void setChannelName(String channelName) {
this.channelName = channelName;
} public int getIsEnable() {
return isEnable;
} public void setIsEnable(int isEnable) {
this.isEnable = isEnable;
} public int getPosition() {
return position;
} public void setPosition(int position) {
this.position = position;
} @Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
NewsChannelBean bean = (NewsChannelBean) o;
if (isEnable != bean.isEnable)
return false;
if (position != bean.position)
return false;
if (channelId != null ? !channelId.equals(bean.channelId) : bean.channelId != null)
return false;
return channelName != null ? channelName.equals(bean.channelName) : bean.channelName == null;
} @Override
public int hashCode() {
int result = channelId != null ? channelId.hashCode() : 0;
result = 31 * result + (channelName != null ? channelName.hashCode() : 0);
result = 31 * result + isEnable;
result = 31 * result + position;
return result;
} @Override
public int compareTo(@NonNull NewsChannelBean o) {
return this.position - o.getPosition();
}
}

2.构造NewsChannelDao处理底层数据增删查改

2.1.获取可以写数据库的实例。 

    private SQLiteDatabase db;

    public NewsChannelDao() {
this.db = DatabaseHelper.getDatabase();
}

  这个DatabaseHelper是一个自定义的数据库帮助类,用来产生可以写数据库的实例。而且要求加上同步锁。

  //这里是DatabaseHelper类
  public static synchronized SQLiteDatabase getDatabase() {
if (db == null) {
db = getInstance().getWritableDatabase();
}
return db;
}

  

2.2.添加初始数据。

public void addInitData() {
String categoryId[] = InitApp.AppContext.getResources().getStringArray
                                      (R.array.mobile_news_id);
String categoryName[] = InitApp.AppContext.getResources().getStringArray
                                      (R.array.mobile_news_name);
for (int i = 0; i < 8; i++) {
add(categoryId[i], categoryName[i], Constant.NEWS_CHANNEL_ENABLE, i);
}
for (int i = 8; i < categoryId.length; i++) {
add(categoryId[i], categoryName[i], Constant.NEWS_CHANNEL_DISABLE, i);
}
}

  这个R.array.mobile_news_id是自定义的新闻类别id

  这个R.array.mobile_news_name是自定义的新闻名称

2.3.添加add方法。

public boolean add(String channelId, String channelName, int isEnable, int position) {
ContentValues values = new ContentValues();
values.put(NewsChannelTable.ID, channelId);
values.put(NewsChannelTable.NAME, channelName);
values.put(NewsChannelTable.IS_ENABLE, isEnable);
values.put(NewsChannelTable.POSITION, position);
long result = db.insert(NewsChannelTable.TABLENAME, null, values);
return result != -1;
}

  这里用ContentValues整理插入数据库的数据。

2.4.查询方法。 

 public List<NewsChannelBean> query(int isEnable) {
Cursor cursor = db.query(NewsChannelTable.TABLENAME, null,
      NewsChannelTable.IS_ENABLE + "=?", new String[]{isEnable + ""}, null, null, null);
List<NewsChannelBean> list = new ArrayList<>();
while (cursor.moveToNext()) {
NewsChannelBean bean = new NewsChannelBean();
bean.setChannelId(cursor.getString(NewsChannelTable.ID_ID));
bean.setChannelName(cursor.getString(NewsChannelTable.ID_NAME));
bean.setIsEnable(cursor.getInt(NewsChannelTable.ID_ISENABLE));
bean.setPosition(cursor.getInt(NewsChannelTable.ID_POSITION));
list.add(bean);
}
cursor.close();
return list;
}

2.5.查询所有

public List<NewsChannelBean> queryAll() {
Cursor cursor = db.query(NewsChannelTable.TABLENAME, null, null, null, null, null, null);
List<NewsChannelBean> list = new ArrayList<>();
while (cursor.moveToNext()) {
NewsChannelBean bean = new NewsChannelBean();
bean.setChannelId(cursor.getString(NewsChannelTable.ID_ID));
bean.setChannelName(cursor.getString(NewsChannelTable.ID_NAME));
bean.setIsEnable(cursor.getInt(NewsChannelTable.ID_ISENABLE));
bean.setPosition(cursor.getInt(NewsChannelTable.ID_POSITION));
list.add(bean);
}
cursor.close();
return list;
}

2.6.移除所有

    public boolean removeAll() {
int result = db.delete(NewsChannelTable.TABLENAME, null, null);
return result != -1;
}

2.7.NewsChannelDao源代码

package com.jasonjan.headnews.database.dao;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import com.jasonjan.headnews.R;
import com.jasonjan.headnews.bean.news.NewsChannelBean;
import com.jasonjan.headnews.database.helper.DatabaseHelper;
import com.jasonjan.headnews.database.table.NewsChannelTable;
import com.jasonjan.headnews.global.Constant;
import com.jasonjan.headnews.global.InitApp; import java.util.ArrayList;
import java.util.List; /**
* Created by JasonJan on 2017/12/4.
*/ public class NewsChannelDao {
private SQLiteDatabase db;
public NewsChannelDao(){
this.db= DatabaseHelper.getDatabase();
} public void addInitData(){
String categoryId[]= InitApp.AppContext.getResources().getStringArray(R.array.mobile_news_id);
String categoryName[]=InitApp.AppContext.getResources().getStringArray(R.array.mobile_news_name);
for(int i=0;i<8;i++){
add(categoryId[i], categoryName[i], Constant.NEWS_CHANNEL_ENABLE, i);
}
for (int i = 8; i < categoryId.length; i++) {
add(categoryId[i], categoryName[i], Constant.NEWS_CHANNEL_DISABLE, i);
}
} public boolean add(String channelId,String channelName,int isEnable,int position){
ContentValues values=new ContentValues();
values.put(NewsChannelTable.ID,channelId);
values.put(NewsChannelTable.NAME,channelName);
values.put(NewsChannelTable.IS_ENABLE,isEnable);
values.put(NewsChannelTable.POSITION,position);
long result=db.insert(NewsChannelTable.TABLENAME,null,values);
return result!=-1;
} public List<NewsChannelBean> query(int isEnable) {
Cursor cursor = db.query(NewsChannelTable.TABLENAME, null, NewsChannelTable.IS_ENABLE +
"=?", new String[]{isEnable + ""}, null, null, null);
List<NewsChannelBean> list = new ArrayList<>();
while (cursor.moveToNext()) {
NewsChannelBean bean = new NewsChannelBean();
bean.setChannelId(cursor.getString(NewsChannelTable.ID_ID));
bean.setChannelName(cursor.getString(NewsChannelTable.ID_NAME));
bean.setIsEnable(cursor.getInt(NewsChannelTable.ID_ISENABLE));
bean.setPosition(cursor.getInt(NewsChannelTable.ID_POSITION));
list.add(bean);
}
cursor.close();
return list;
} public List<NewsChannelBean> queryAll() {
Cursor cursor = db.query(NewsChannelTable.TABLENAME, null, null, null, null, null, null);
List<NewsChannelBean> list = new ArrayList<>();
while (cursor.moveToNext()) {
NewsChannelBean bean = new NewsChannelBean();
bean.setChannelId(cursor.getString(NewsChannelTable.ID_ID));
bean.setChannelName(cursor.getString(NewsChannelTable.ID_NAME));
bean.setIsEnable(cursor.getInt(NewsChannelTable.ID_ISENABLE));
bean.setPosition(cursor.getInt(NewsChannelTable.ID_POSITION));
list.add(bean);
}
cursor.close();
return list;
} public void updateAll(List<NewsChannelBean> list) {
} public boolean removeAll() {
int result = db.delete(NewsChannelTable.TABLENAME, null, null);
return result != -1;
}
}

3.BaseListFragment抽象类

3.1.最底层的接口IBaseView<T>

  不理解泛型==>参考这篇文章理解java泛型的作用。

  

  这里是这样定义这个接口的。 

public interface IBaseView<T> {

    /**
* 显示加载动画
*/
void onShowLoading(); /**
* 隐藏加载
*/
void onHideLoading(); /**
* 显示网络错误
*/
void onShowNetError(); /**
* 设置 presenter
*/
void setPresenter(T presenter); /**
* 绑定生命周期
*/
<T> LifecycleTransformer<T> bindToLife();
}

  定义了5个方法,在实现类中必须要实现的方法(除了抽象类不必全部实现)

  • onShowLoading==>显示加载动画
  • onHideLoading==>隐藏加载动画
  • onShowNetError==>显示网络错误
  • setPresenter==>设置presenter
  • bindToLife==>绑定生命周期

  <T>   LifecycleTransformer<T> bindToLife()这个方法:

    前面的<T> 只是说明这是一个泛型方法。

    返回值是LifecycleTransformer<T> 是RxJava中定义的一个类型。

3.2.同理看一下IBaseListView<T>

public interface IBaseListView<T> extends IBaseView<T> {

    /**
* 显示加载动画
*/
void onShowLoading(); /**
* 隐藏加载
*/
void onHideLoading(); /**
* 显示网络错误
*/
void onShowNetError(); /**
* 设置 presenter
*/
void setPresenter(T presenter); /**
* 绑定生命周期
*/
<T> LifecycleTransformer<T> bindToLife(); /**
* 设置适配器
*/
void onSetAdapter(List<?> list); /**
* 加载完毕
*/
void onShowNoMore();
}

  明白了:接口是可以继承的。

  这里多了两个方法:onSetAdapter(List<?> list)==>顾名思义,这里设置的一定是一个List类型的适配器。

           onShowNoMore()==>这里多了一个加载完毕的方法。

3.3.IBasePresenter接口类

  这个接口相对于比较简单。

public interface IBasePresenter {

    /**
* 刷新数据
*/
void doRefresh(); /**
* 显示网络错误
*/
void doShowNetError();
}
  • doRefresh==>用来刷新数据。
  • doShowNetError==>显示网络错误。

3.4.抽象类BaseFragment<T extends IBasePresenter>

  T就是就是继承了IBasePresenter接口的一个泛型。

  但是这个BaseFragment中并没有实现接口中的方法,因为它本身也是一个抽象类。方法交给子类去实现吧。

  所以这个抽象类中定义了一些方法。

  这里面又重写3个抽象方法。 

 /**
* 绑定布局文件
*
* @return 布局文件ID
*/
protected abstract int attachLayoutId(); /**
* 初始化视图控件
*/
protected abstract void initView(View view); /**
* 初始化数据
*/
protected abstract void initData() throws NullPointerException;

  attachLayoutId==>可以获取布局文件ID

  initView==>初始化视图控件

  initData==>初始化数据

  然后定义了一个沟通者presenter,采用泛型定义。

  protected T presenter;

  

  当然只实现了一个IBaseView接口方法

    /**
* 绑定生命周期
*/
@Override
public <T> LifecycleTransformer<T> bindToLife() {
return bindUntilEvent(FragmentEvent.DESTROY);
}

  所以这就是为什么不把IBaseView中的接口方法全部加到BaseFragment中,而是分了一个抽象类+一个接口。

 

  在BaseFragment抽象类中要进行初始化Toolbar 

 /**
* 初始化 Toolbar
*/
protected void initToolBar(Toolbar toolbar, boolean homeAsUpEnabled, String title) {
((BaseActivity) getActivity()).initToolBar(toolbar, homeAsUpEnabled, title);
}

  传入的参数有:标题栏布局,是否有左箭头,title。

  首先执行onCreate==>设置presenter

 @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setPresenter(presenter);
}

  然后执行onCreateView==>initView(view)==>initData()

 @Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                                @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(attachLayoutId(), container, false);
initView(view);
initData();
return view;
}

  得到fragment视图布局id,初始化视图为抽象函数,在子类中实现。初始化数据为抽象函数,在子类中实现。

3.5.懒加载碎片LazyLoadFragment<T extends IBasePresenter>

 继承关系: 

public abstract class LazyLoadFragment<T extends IBasePresenter> extends BaseFragment<T>

  继承了上面刚分析的抽象类,这个也是一个抽象类,不必实现所有的抽象函数。

  先分析三个boolean

  protected boolean isViewInitiated;
protected boolean isVisibleToUser;
protected boolean isDataInitiated;
  • isViewInitiated==>视图是否初始化。
  • isVisibleToUser==>是否对用户可见。
  • isDataInitiated==>数据是否初始化完成。

  

  然后执行onCreate 

   @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

  然后执行onActivityCreated

  @Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isViewInitiated = true;
prepareFetchData();
}

  确定视图已经初始化了。修改isViewInitiated为true。

  然后执行准备取数据。 

    public boolean prepareFetchData() {
return prepareFetchData(false);
}

  然后定义一个带有一个参数(是否强制刷新的变量)的取数据的函数。

 public boolean prepareFetchData(boolean forceUpdate) {
if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
fetchData();
isDataInitiated = true;
return true;
}
return false;
}

  如果对用户可见、视图初始化完毕、(数据未初始化完毕或强制刷新)==>执行取数据的抽象函数。

  执行定义的抽象函数==>fetchData() 

 public abstract void fetchData();

  最后将数据是否初始化完毕的变量赋值为true。

  

3.6.抽象函数BaseListFragment<T extends IBasePresenter>

  对于类名称加一个T,或者加一个<T extends ?>的理解

  可以参考一下这篇文章:java中的泛型类及其使用。

  

  

  看一下继承方式。

public abstract class BaseListFragment<T extends IBasePresenter> 

            extends LazyLoadFragment<T> 
    
            implements IBaseListView<T>, SwipeRefreshLayout.OnRefreshListener

  继承了懒加载碎片==>主要抽象函数fetchData()取数据

  实现了IBaseListView<T>==>主要有7个抽象方法。

  实现了SwipeRefreshLayout.OnRefreshListener==>主要方法onRefresh()刷新数据

  #调用了开源框架==>Multitype框架==>处理复杂的视图列表

  github地址:https://github.com/drakeet/MultiType

  

  预览一下成员变量。

  public static final String TAG = "BaseListFragment";
protected RecyclerView recyclerView;
protected SwipeRefreshLayout swipeRefreshLayout;
protected MultiTypeAdapter adapter;
protected Items oldItems = new Items();
protected boolean canLoadMore = false;
protected Observable<Integer> observable;

  这里的MultiTypeAdapter+Items 都是第三方库框架,方便处理复杂的视图。

  这里的Observable<Integer>是RxJava框架。

  实现了BaseFragment中的抽象函数==>attachLayoutId()绑定布局文件。 

  @Override
protected int attachLayoutId() {
return R.layout.fragment_list;
}

  这个布局文件是自己早就写好了的。

<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/windowBackground"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> <android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadeScrollbars="true"
android:scrollbarFadeDuration="1"
android:scrollbars="vertical"
app:layoutManager="LinearLayoutManager">
</android.support.v7.widget.RecyclerView> </android.support.v4.widget.SwipeRefreshLayout> </LinearLayout>

  预览效果如下:

  

  初始化视图initView(View view) 

 @Override
protected void initView(View view) {
recyclerView = view.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true); swipeRefreshLayout = view.findViewById(R.id.refresh_layout);
swipeRefreshLayout.setColorSchemeColors(SettingUtil.getInstance().getColor());
swipeRefreshLayout.setOnRefreshListener(this);
}

  recyclerView是列表。

  swipeRefreshLayout是包裹列表的一个可以刷新的布局。

  实现IBaseView<T>接口中的显示加载动画+隐藏加载动画函数。

 @Override
public void onShowLoading() {
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
}
});
} @Override
public void onHideLoading() {
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(false);
}
});
}

  实现懒加载碎片中的抽象函数==>fetchData()来取数据。

 @Override
public void fetchData() {
observable = RxBus.getInstance().register(BaseListFragment.TAG);
observable.subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
adapter.notifyDataSetChanged();
}
});
}

  这里进行订阅。拿到数据,然后刷新。

  

  实现IBaseView<T>中的接口函数onShowNetError。 

 @Override
public void onShowNetError() {
Toast.makeText(getActivity(), R.string.network_error, Toast.LENGTH_SHORT).show();
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.setItems(new Items());
adapter.notifyDataSetChanged();
canLoadMore = false;
}
});
}

  这里提示用户,网络不给力。

  生命周期函数onResume。 

@Override
public void onResume() {
super.onResume();
// 设置下拉刷新的按钮的颜色
swipeRefreshLayout.setColorSchemeColors(SettingUtil.getInstance().getColor());
}

  

  实现IBaseListView额外的两个函数中的一个onShowNoMore加载完毕接口函数。

@Override
public void onShowNoMore() {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (oldItems.size() > 0) {
Items newItems = new Items(oldItems);
newItems.remove(newItems.size() - 1);
newItems.add(new LoadingEndBean());
adapter.setItems(newItems);
adapter.notifyDataSetChanged();
} else if (oldItems.size() == 0) {
oldItems.add(new LoadingEndBean());
adapter.setItems(oldItems);
adapter.notifyDataSetChanged();
}
canLoadMore = false;
}
});
}

  实现刷新框的onRefresh 

@Override
public void onRefresh() {
int firstVisibleItemPosition = ((LinearLayoutManager)
                recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
if (firstVisibleItemPosition == 0) {
presenter.doRefresh();
return;
}
recyclerView.scrollToPosition(5);
recyclerView.smoothScrollToPosition(0);
}

  先判断第一次可见的位置在最前面,也就是一开始没有数据的时候。

  如果是第一次点进去的话,必然要执行刷新。

  然后recyclerView回到顶部。

  最后执行一个onDestroy来反注册。

    @Override
public void onDestroy() {
RxBus.getInstance().unregister(BaseListFragment.TAG, observable);
super.onDestroy();
}

  有个非常非常坑爹的地方 
  RxFragment有两个类型:
  

import com.trello.rxlifecycle2.components.RxFragment;
import com.trello.rxlifecycle2.components.support.RxFragment;

  RxFragment==>类型是app.Fragment

    support.RxFragment==>类型是v4.app.Fragment 是兼容的。

TouTiao开源项目 分析笔记6的更多相关文章

  1. TouTiao开源项目 分析笔记2

    1.Constant常量定义类 1.1.源代码 public class Constant { public static final String USER_AGENT_MOBILE = " ...

  2. TouTiao开源项目 分析笔记4==>一个简单APP 整体常用框架

    1.效果预览 1.1.如下图所以,到目前为止所有的功能. 2.从InitApp开始->SplashActivity->MainActivity 2.1.InitApp源代码.这是整个项目的 ...

  3. TouTiao开源项目 分析笔记15 新闻详情之两种类型的实现

    1.预览效果 1.1.首先看一下需要实现的效果. 第一种,文字类型新闻. 第二种,图片类型新闻. 1.2.在NewsArticleTextViewBinder中设置了点击事件 RxView.click ...

  4. TouTiao开源项目 分析笔记12 从总体到局部 构建视频主页面

    1.构建视频主列表的整体碎片VideoTabLayout 1.1.首先创建一个VideoTabLayout package com.jasonjan.headnews.module.video; im ...

  5. TouTiao开源项目 分析笔记10 实现通用普通文章片段页面

    1.RxJava的Observable数据操作符总结 1.1.Map操作符 Map操作符对原始Observable发射的没一项数据应用一个你选择的函数, 然后返回一个发射这些结果的Observable ...

  6. TouTiao开源项目 分析笔记1

    1.InitApp==>项目的入口Application 1.1.继承了MultiDexApplication 超过65K方法的APP,会遇到65535的错误.原因就是为了支持比较大型的APP而 ...

  7. TouTiao开源项目 分析笔记18 视频详情页面

    1.效果预览 1.1.需要做到的真实效果 1.2.触发的点击事件 在MediaArticleVideoViewBinder的每一个item点击事件中: VideoContentActivity.lau ...

  8. TouTiao开源项目 分析笔记17 新闻媒体专栏

    1.效果预览 1.1.要实现的效果 1.2.如何调转到新闻媒体专栏 点击右上角的用户图标. 在新闻详情页面的Fragment的菜单点击事件中触发. case R.id.action_open_medi ...

  9. TouTiao开源项目 分析笔记16 新闻评论

    1.要达到的效果 1.1.主要效果图 点击了标题栏的消息图标后,然后会跳转到评论详情的页面. 1.2.触发的点击事件 在新闻详情的片段中的菜单点击事件中 设置上方标题栏的消息标的监听事件 case R ...

随机推荐

  1. 关于基于Linphone的视频通话Android端开发过程中遇到的问题

    关于基于Linphone的视频通话Android端开发过程中遇到的问题 运用开源项目Linphone的SDK进行开发,由于是小组进行开发,我主要负责的是界面部分. 由于当时是初学Android开发,对 ...

  2. manjaro安装后你需要做的配置

    1.切换中国源 sudo gedit /etc/pacman-mirrors.conf 如果提示没有gedit , 则执行命令 : sudo pacman -S gedit 修改如下地方为中国: On ...

  3. FTP无法连接可能是安全狗设置的原因

    防火墙已添加某端口,但仍无法连接.可能是安全狗导致的. 如果安装了安全狗,请按照以下步骤设置:

  4. 怎样下载YouTube 4K视频

    随着科技的进步,人们生活水平的提高,视频的清晰度也越来越高,以前那个观看模糊视频的时代已经一去不复返了.从最开始的720P和1080P高清视频,再到2K,进而到如今的4K(即3840×2160)极清视 ...

  5. Jmeter入门4 添加断言 判断响应数据是否符合预期

    发出请求之后,通过添加断言可以判断响应数据是否是我们的预期结果. 1 在Jmeter中发送一个登录的http请求(参数故意输入错误).结果肯定是登陆失败啦. 但结果树中http请求的图标显示‘绿色’表 ...

  6. 【转载】#335 - Accessing a Derived Class Using a Base Class Variable

    You can use a variable whose type is a base class to reference instances of a derived class. However ...

  7. POJ-1509 Glass Beads---最小表示法模板

    题目链接: https://vjudge.net/problem/POJ-1509 题目大意: 给你一个循环串,然后找到一个位置,使得从这个位置开始的整个串字典序最小. 解题思路: 最小表示法模板 注 ...

  8. ARM是CPU体系结构

    https://zhidao.baidu.com/question/680620766286548532.html ARM是一种使用精简指令(RISC)的CPU,有别于英特尔的复杂指令(CISC) x ...

  9. 将命令的输出生成一个Web页面

    解决方法: ConvertTo-Html 命令: 生成一个HTML表格来代表命令的输出,为你提供的每个对象创建一行,在每行中,Powershell会创建代表对象属性的值. 实现效果:

  10. Oracle 11g基础

    一.打开.关闭数据库 sqlplus "/as sysdba" connect system/manager as sysdba 关闭 shutdown immediate; 打开 ...