您的位置:首页 > 其它

个人博客客户端——My CSDN 的实现(3)

2016-06-05 14:43 246 查看
在这一篇博客开始就要开始介绍客户端的具体开发了

工程打开后的具体目录如下所示



各位看官在建立自己的工程时,需要将“ViewPagerIndicator”和“MaterialRefreshLayout”两个依赖库导入项目中,此外还要从GitHub加载Fresco。

加载Fresco的方法很简单,在build.gradle文件中加入以下语句即可

compile 'com.facebook.fresco:fresco:0.10.0'


而实际上,工程中需要用到的所有依赖项都写在了“dependencies”中。



My CSDN的主界面是可以滑动切换的,这就用到了support.v4包下的ViewPager控件,在上边有着多个指示标签,用到了“viewpagerindicator”依赖库下的“TabPageIndicator”控件。

并用到了侧边栏“DrawerLayout ”控件。

主界面布局文如下:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical">

<include layout="@layout/top" />

<com.viewpagerindicator.TabPageIndicator
android:id="@+id/id_indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ed130f" />

<android.support.v4.view.ViewPager
android:id="@+id/id_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#eceff2" />
</LinearLayout>

<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header"
app:itemTextColor="#000"
app:menu="@menu/activity_main_drawer" />

</android.support.v4.widget.DrawerLayout>


而要使用ViewPager控件,需要为其指定适配器,因此需要自定义类继承FragmentPagerAdapter。

/**
* 页面适配器
* Created by ZY on 2016/5/25.
*/
public class PagerAdapter extends FragmentPagerAdapter {

/**
* 标签
*/
public static String[] TAGS = new String[]{
"最新", Constant.DIRECTORY_1, Constant.DIRECTORY_2, Constant.DIRECTORY_3, Constant.DIRECTORY_4, Constant.DIRECTORY_5, Constant.DIRECTORY_6
};

public PagerAdapter(FragmentManager fm) {
super(fm);
}

@Override
public Fragment getItem(int position) {
TabFragment tabFragment = new TabFragment();
Bundle bundle = new Bundle();
bundle.putInt("position", position);
tabFragment.setArguments(bundle);
return tabFragment;
}

@Override
public int getCount() {
return TAGS.length;
}

@Override
public CharSequence getPageTitle(int position) {
return TAGS[position];
}
}


需要注意,在getItem()方法中,我们需要返回一个“TabFragment”对象,TabFragment即可以切换显示的页面。还要向该对象传入一个整形参数“position”,如果我们直接为TabFragmnet设定一个如下所示的带参数的构造函数

public TabFragment(int position){
this.position=position;
}


这似乎也可以起到传递带参数的作用,不过Android Studio会建议你采用Bundle来传递参数,因为该构造函数可能会破坏Fragment的生命周期。

因此,在MianActivity.java文件中,主要任务就是初始化各个组件,并设定各适配器,首次打开自动加载数据被解析等。

使用Fresco必须先初始化,这里使用的是默认设置。

public class MainActivity extends FragmentActivity implements NavigationView.OnNavigationItemSelectedListener {

private DrawerLayout mDrawerLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//使用Fresco前必需先初始化
Fresco.initialize(this);
setContentView(R.layout.activity_main);
Constant.init(MainActivity.this);
initView();
}

private void initView() {
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
SimpleDraweeView top_image = (SimpleDraweeView) findViewById(R.id.top_image);
ViewPager mViewPager = (ViewPager) findViewById(R.id.id_viewpager);
TabPageIndicator mTabPageIndicator = (TabPageIndicator) findViewById(R.id.id_indicator);
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);

PagerAdapter mPageAdapter = new PagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mPageAdapter);
//将两个空间联系起来,并设定默认显示第一页
mTabPageIndicator.setViewPager(mViewPager, 0);

top_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mDrawerLayout.openDrawer(GravityCompat.START);
}
});

navigationView.setNavigationItemSelectedListener(this);
new LoadImageAsync().execute();
}


当中,
new LoadImageAsync().execute();
语句是用来加载头像用的


在导航栏顶部和侧边栏顶部,共有两个头像,在打开App时,LoadImageAsync的作用就是用来联网加载头像的。当然,该头像只会联网加载一次,以后再次打开app时,会直接从本地缓存中加载图片,并能将图片设为圆形,这都是Fresco的强大功能。只有当博主对象改变了时,才会重新从网上加载图片。

/**
* 用来加载主界面顶部头像以及侧边栏顶部头像
* 当前设置为仅主界面顶部显示博主头像
* 侧边栏为本地默认头像
* 如果去掉注释代码,则可以均显示为博主头像
*/
class LoadImageAsync extends AsyncTask<Void, Void, String> {

@Override
protected String doInBackground(Void... params) {
BlogAuthor blogAuthor = JsoupUtil.getBlogAutoMessage();
String avatarUrl = "";
if (blogAuthor != null) {
avatarUrl = blogAuthor.getAvatarUrl();
}
return avatarUrl;
}

@Override
protected void onPostExecute(String avatarUrl) {
Uri uri = Uri.parse(avatarUrl);
SimpleDraweeView top_image = (SimpleDraweeView) findViewById(R.id.top_image);
// SimpleDraweeView nav_head_image = (SimpleDraweeView) findViewById(R.id.nav_head_image);
top_image.setImageURI(uri);
// nav_head_image.setImageURI(uri);
}
}


滑动标签完成了,接下来就要为Fragment填充内容了,即显示博客列表。在这里我不打算使用ListView,用的是RecyclerView。此外,还要使用能够支持RecyclerView下拉刷新的MaterialRefreshLayout

博客列表的布局如下,可以看到就是在MaterialRefreshLayout当中包裹一个RecyclerView而已

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:orientation="vertical">

<com.cjj.MaterialRefreshLayout
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:isLoadMore="true"
app:overlay="true"
app:progress_colors="@array/material_colors"
app:progress_size_type="normal"
app:wave_color="#60dcd7d7"
app:wave_height_type="normal"
app:wave_show="true">

<android.support.v7.widget.RecyclerView
android:id="@+id/another_content_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</com.cjj.MaterialRefreshLayout>

</FrameLayout>


看上图可以知道,RecyclerView的每一项包含了三个部分,即标题,简介,博客信息,使用的即是三个TextView。因此,还需要为子项设定布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="150dp"
android:orientation="vertical"
android:padding="6dp">

<TextView
android:id="@+id/blog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/black"
android:textSize="20sp"
android:textStyle="bold" />

<TextView
android:id="@+id/blog_introduction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="4"
android:textColor="@android:color/black"
android:textSize="16sp" />

<TextView
android:id="@+id/blog_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginRight="6dp"
android:textColor="@android:color/black"
android:textSize="13sp" />

</LinearLayout>


然后,再为RecyclerView指定一个适配器
BlogItemListAdapter
,让其继承自
RecyclerView.Adapter<BlogItemHolder>
BlogItemHolder
也是继承自
RecyclerView.ViewHolder
的。

public class BlogItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

public TextView blog_title;

public TextView blog_introduction;

public TextView blog_msg;

public ItemOnClickListener itemOnClickListener;

public BlogItemHolder(View itemView) {
super(itemView);
blog_title = (TextView) itemView.findViewById(R.id.blog_title);
blog_introduction = (TextView) itemView.findViewById(R.id.blog_introduction);
blog_msg = (TextView) itemView.findViewById(R.id.blog_msg);
itemView.setOnClickListener(this);
}

public void setItemOnClickListener(ItemOnClickListener itemOnClickListener) {
this.itemOnClickListener = itemOnClickListener;
}

@Override
public void onClick(View v) {
if (itemOnClickListener != null) {
itemOnClickListener.OnItemClickListener(getAdapterPosition());
}
}
}


当中,
ItemOnClickListener
是一个自定义的接口,因为RecvclerView没有提供子项点击监听方法,所以需要自己建立一个回调函数,用于在用户点击子项时跳转到具体博客内容页面。

BlogItemListAdapter
中需要实现的方法有以下三个,具体实现看我提供的工程代码

//注意该方法的第二个参数viewType,在后边设定博客内适配器时会有很大用处
onCreateViewHolder(ViewGroup parent, int viewType)

onBindViewHolder(BlogItemHolder holder, int position)

getItemCount()


需要重点说的是自定义的接口
ItemOnClickListener
,当中仅有一个
OnItemClickListener
方法,并需要一个参数position,即点击的子项在列表中的行数。

/**
* Created by ZY on 2016/5/29.
* 自定义博客列表点击事件监听接口
*/
public interface ItemOnClickListener {
void OnItemClickListener(int position);
}


BlogItemListAdapter
中声明该接口并实现其方法,
Constant.USE_WEB
用于判断是否启用WebView打开博客,这里无需理会。可以看到,用户点击后会跳转到新的Activity,用于呈现博客内容,传递的参数url是博客链接。

public ItemOnClickListener itemOnClickListener = new ItemOnClickListener() {
@Override
public void OnItemClickListener(int position) {
String url = blogIntroductionList.get(position).getUrl();
Intent intent;
if (!Constant.USE_WEB) {
intent = new Intent(context, ContentByJsoupActivity.class);
} else {
intent = new Intent(context, ContentByWebActivity.class);
}
intent.putExtra("url", url);
context.startActivity(intent);
}
};


onBindViewHolder(BlogItemHolder holder, int position)
方法中再将
itemOnClickListener
对象传递过去,这样就利用回调方法实现了对RecyclerCiew子项的点击事件的监听了。

holder.setItemOnClickListener(itemOnClickListener);


这一篇博客先讲到这里,下一篇再来介绍博客内容该如何呈现~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: