上一篇界面初步设计完之后就是获取微博的详情界面,包括所关注的用户的微博界面,评论界面,转发界面等等。
还是先上我的效果图吧。
ps:外观我参考aisen微博客户端,也是一个Material Design的客户端,做的非常不错。
附上他的项目git地址:https://github.com/wangdan/AisenWeiBo
第一步,设计UI效果,这里我是用的是google的SwiprefreshLayout控件,加上ListView展示微博信息列表的。布局里分为两个模块,一个原文内容,一个转发内容,如果不是转发内容,就直接展示原文内容。
附上fragment_main.xml代码
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fab="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/theme_background"
android:orientation="vertical" >
<com.sdf.simplebeautyweibo.widget.SwipeRefreshLayout
android:id="@+id/swiperefreshlayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</com.sdf.simplebeautyweibo.widget.SwipeRefreshLayout>
<com.melnykov.fab.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:background="@color/theme_color"
android:src="@drawable/ic_refresh_light" />
</FrameLayout>
之后就是适配器的设计,item的布局就是如图所示:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rly_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingBottom="4dp"
android:descendantFocusability="beforeDescendants"
android:paddingLeft="8dip"
android:paddingRight="8dip"
android:paddingTop="6dp" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/timeline_publish_single_normal"
android:orientation="vertical"
android:paddingLeft="@dimen/item_padding"
android:paddingRight="@dimen/item_padding"
android:paddingTop="@dimen/item_padding" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<com.sdf.simplebeautyweibo.widget.RoundImageView
android:id="@+id/iv_head"
android:layout_width="42dip"
android:layout_height="42dip"
android:layout_marginRight="8dip" />
<TextView
android:id="@+id/tv_name"
style="@style/TextTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/iv_head"
android:text="名字" />
<TextView
android:id="@+id/tv_time"
style="@style/TextSubhead"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_name"
android:layout_marginTop="2dip"
android:layout_toRightOf="@id/iv_head"
android:text="来自简美微博 2015-01-26" />
<TextView
android:id="@+id/tv_content"
style="@style/TextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/iv_head"
android:layout_marginBottom="4dip"
android:layout_marginTop="10dip"
android:lineSpacingExtra="2dip"
android:text="哈哈,我今天发了条微博" />
<!-- android:background="@drawable/timeline_profile_question_single_normal" -->
<RelativeLayout
android:id="@+id/layRe"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_content"
android:layout_marginLeft="-10dip"
android:layout_marginRight="-10dip"
android:orientation="vertical"
android:paddingBottom="4dip"
android:paddingLeft="10dip"
android:paddingRight="10dip" >
<View
android:id="@+id/view_line"
android:layout_width="match_parent"
android:layout_height="1.5dip"
android:layout_marginBottom="8dip"
android:background="@color/divider" />
<TextView
android:id="@+id/tv_recontent"
style="@style/TextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/view_line"
android:lineSpacingExtra="2dip" />
</RelativeLayout>
</RelativeLayout>
<!-- 展示图片 -->
<com.sdf.simplebeautyweibo.widget.NineGridImageView
android:id="@+id/grid_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:imgGap="4dp"
app:showStyle="fill"
app:singleImgSize="120dp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="35dip"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center_vertical"
android:orientation="horizontal" >
<LinearLayout
android:id="@+id/lly_like"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="0dip"
android:gravity="center"
android:orientation="horizontal"
android:paddingBottom="2dip" >
<ImageView
android:id="@+id/iv_like"
android:layout_width="24dip"
android:layout_height="20dip"
android:src="@drawable/src_like" />
<TextView
android:id="@+id/tv_like"
style="@style/TextBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="4dip"
android:text="1人赞了"
android:textColor="@color/text_54"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/lly_comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:gravity="center"
android:orientation="horizontal" >
<ImageView
android:layout_width="24dip"
android:layout_height="20dip"
android:src="@drawable/timeline_icon_comment" />
<TextView
android:id="@+id/tv_comment"
style="@style/TextBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="100"
android:textColor="@color/text_54"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/lly_repost"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dip"
android:layout_toLeftOf="@id/lly_comment"
android:gravity="center"
android:orientation="horizontal" >
<ImageView
android:layout_width="24dip"
android:layout_height="20dip"
android:src="@drawable/timeline_icon_redirect" />
<TextView
android:id="@+id/tv_repost"
style="@style/TextBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="100"
android:textColor="@color/text_54"
android:textSize="12sp" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<ImageView
android:id="@+id/iv_menu"
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_alignParentRight="true"
android:paddingBottom="8dip"
android:src="@drawable/selector_card_menu" />
</RelativeLayout>
第二步,设计完界面之后就是调用微博的API了获取数据。我们在之前获取到了token,这个值就是用来请求资源的。很多人不知道怎么做就多看看微博SDK的Demo,里面写好了很多API调用的例子。就是我图里红色圈出来的,这些Activity中都有如何调用API并且解析所获取的数据的,详细我就不说了。各种API看名字就能知道他的作用,常用的有StatusAPI,CommentAPI等。
我只放上部分调用的代码:
//这里就是我们只i前拿到的token值,将它从SharedPreference中取出来
mAccessToken = AccessTokenKeeper.readAccessToken(getActivity());
mStatusApi = new StatusesAPI(getActivity(), Constants.APP_KEY,
mAccessToken);
if (mAccessToken != null && mAccessToken.isSessionValid()) {
mStatusApi.friendsTimeline(0L, 0L, 10, 1, false, 0, false,mListener);
} else {
Toast.makeText(getActivity(), "token不存在,请重新授权",Toast.LENGTH_LONG).show();
}
接下去在定义好的listener回调中将数据解析并且传入到适配器展示。
private RequestListener mListener = new RequestListener() {
@Override
public void onWeiboException(WeiboException e) {
// TODO Auto-generated method stub
ErrorInfo info = ErrorInfo.parse(e.getMessage());
Toast.makeText(getActivity(), info.toString(), Toast.LENGTH_LONG)
.show();
mSwipeRefreshLayout.setRefreshing(false);
mSwipeRefreshLayout.setLoading(false);
}
@Override
public void onComplete(final String response) {
// TODO Auto-generated method stub
if (!TextUtils.isEmpty(response)) {
if (response.startsWith("{\"statuses\"")) {
mList = StatusList.parse(response);
mSwipeRefreshLayout.setRefreshing(false);
mSwipeRefreshLayout.setLoading(false);
Log.i("sdf", mList.toString());
if (mList != null && mList.total_number > 0) {
// mTimeLineDao.add(mList.statusList);
mAdpter = new TimeLineAdapter(getActivity(), mList);
mListView.setAdapter(mAdpter);
}
mSwipeRefreshLayout.setRefreshing(false);
mSwipeRefreshLayout.setLoading(false);
}
}
}
};
适配器的代码如下:
package com.sdf.simplebeautyweibo;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.text.Html;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.sdf.simplebeautyweibo.app.MyApplication;
import com.sdf.simplebeautyweibo.comment.CommentsActivity;
import com.sdf.simplebeautyweibo.photo.PhotoActivity;
import com.sdf.simplebeautyweibo.publish.PublishActivity;
import com.sdf.simplebeautyweibo.user.UserActivity;
import com.sdf.simplebeautyweibo.utils.AccessTokenKeeper;
import com.sdf.simplebeautyweibo.utils.Constants;
import com.sdf.simplebeautyweibo.utils.TimeUtil;
import com.sdf.simplebeautyweibo.utils.WeiboAutolinkUtil;
import com.sdf.simplebeautyweibo.widget.MaterialDialog;
import com.sdf.simplebeautyweibo.widget.NineGridImageView;
import com.sdf.simplebeautyweibo.widget.NineGridImageViewAdapter;
import com.sdf.simplebeautyweibo.widget.RoundImageView;
import com.sina.weibo.sdk.auth.Oauth2AccessToken;
import com.sina.weibo.sdk.exception.WeiboException;
import com.sina.weibo.sdk.net.RequestListener;
import com.sina.weibo.sdk.openapi.legacy.FavoritesAPI;
import com.sina.weibo.sdk.openapi.models.FavoriteList;
import com.sina.weibo.sdk.openapi.models.Status;
import com.sina.weibo.sdk.openapi.models.StatusList;
public class TimeLineAdapter extends BaseAdapter {
private FavoritesAPI mFavouritesAPI;
private Oauth2AccessToken mAccessToken;
private Context mContext;
private LayoutInflater mInflater;
/**
* 微博信息列表
*/
private StatusList mStatusList = new StatusList();
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_default_image)
.showImageOnFail(R.drawable.ic_default_image)
.bitmapConfig(Bitmap.Config.ARGB_8888).cacheInMemory(true)
.cacheOnDisk(true).build();
public TimeLineAdapter(Context context, StatusList list) {
mContext = context;
mInflater = LayoutInflater.from(context);
mStatusList = list;
mAccessToken = AccessTokenKeeper.readAccessToken(context);
mFavouritesAPI = new FavoritesAPI(context, Constants.APP_KEY,
mAccessToken);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mStatusList.statusList == null ? 0 : mStatusList.statusList
.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return mStatusList == null ? null : mStatusList.statusList
.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_timeline, parent,
false);
viewHolder = new ViewHolder();
viewHolder.rly_content = (RelativeLayout) convertView
.findViewById(R.id.rly_content);
viewHolder.lly_comments = (LinearLayout) convertView
.findViewById(R.id.lly_comment);
viewHolder.lly_repost = (LinearLayout) convertView
.findViewById(R.id.lly_repost);
viewHolder.roundImageView = (RoundImageView) convertView
.findViewById(R.id.iv_head);
viewHolder.tv_name = (TextView) convertView
.findViewById(R.id.tv_name);
viewHolder.tv_time = (TextView) convertView
.findViewById(R.id.tv_time);
viewHolder.tv_content = (TextView) convertView
.findViewById(R.id.tv_content);
viewHolder.tv_reContent = (TextView) convertView
.findViewById(R.id.tv_recontent);
viewHolder.tv_like = (TextView) convertView
.findViewById(R.id.tv_like);
viewHolder.tv_repost = (TextView) convertView
.findViewById(R.id.tv_repost);
viewHolder.tv_comment = (TextView) convertView
.findViewById(R.id.tv_comment);
viewHolder.iv_menu = (ImageView) convertView
.findViewById(R.id.iv_menu);
viewHolder.view_line = (View) convertView
.findViewById(R.id.view_line);
viewHolder.nineGridView = (NineGridImageView) convertView
.findViewById(R.id.grid_image);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
final Status mStatus = mStatusList.statusList.get(position);
ImageLoader.getInstance().displayImage(mStatus.user.avatar_hd,
viewHolder.roundImageView, options);
viewHolder.tv_name.setText(mStatus.user.screen_name);
String time = "";
if (!TextUtils.isEmpty(mStatus.created_at)) {
time = TimeUtil.convDate(mStatus.created_at);
}
String from = "";
if (!TextUtils.isEmpty(mStatus.source)) {
from = String.format("%s", Html.fromHtml(mStatus.source));
}
viewHolder.tv_time.setText(time + " " + from);
viewHolder.tv_content.setText(WeiboAutolinkUtil.Autolink(mStatus.text,
mContext));
if (mStatus.retweeted_status != null) {
viewHolder.tv_reContent.setVisibility(View.VISIBLE);
viewHolder.view_line.setVisibility(View.VISIBLE);
String reUserName = mStatus.retweeted_status.user.screen_name;
viewHolder.tv_reContent.setText(WeiboAutolinkUtil.Autolink("@"
+ reUserName + " :" + mStatus.retweeted_status.text,
mContext));
viewHolder.nineGridView.setVisibility(View.VISIBLE);
viewHolder.nineGridView
.setAdapter(new NineGridImageViewAdapter<String>() {
@Override
protected void onDisplayImage(Context context,
ImageView imageView, String t) {
// TODO Auto-generated method stub
ImageLoader.getInstance().displayImage(t,
imageView, options);
}
protected void onItemImageClick(Context context,
int index, java.util.List<String> list) {
Intent intent = new Intent(mContext,
PhotoActivity.class);
if (mStatus.retweeted_status != null) {
MyApplication.getInstance().setStatus(
mStatus.retweeted_status);
} else {
if (mStatus.pic_urls != null) {
MyApplication.getInstance().setStatus(
mStatus);
}
}
intent.putExtra("index", index);
mContext.startActivity(intent);
};
});
viewHolder.nineGridView
.setImagesData(mStatus.retweeted_status.pic_urls);
} else {
if (mStatus.pic_urls != null) {
viewHolder.tv_reContent.setVisibility(View.GONE);
viewHolder.view_line.setVisibility(View.GONE);
viewHolder.nineGridView.setVisibility(View.VISIBLE);
viewHolder.nineGridView
.setAdapter(new NineGridImageViewAdapter<String>() {
@Override
protected void onDisplayImage(Context context,
ImageView imageView, String t) {
// TODO Auto-generated method stub
ImageLoader.getInstance().displayImage(t,
imageView, options);
}
protected void onItemImageClick(Context context,
int index, java.util.List<String> list) {
Intent intent = new Intent(mContext,
PhotoActivity.class);
if (mStatus.pic_urls != null) {
MyApplication.getInstance().setStatus(
mStatus);
}
intent.putExtra("index", index);
mContext.startActivity(intent);
};
});
viewHolder.nineGridView.setImagesData(mStatus.pic_urls);
} else {
viewHolder.tv_reContent.setVisibility(View.GONE);
viewHolder.view_line.setVisibility(View.GONE);
viewHolder.nineGridView.setVisibility(View.GONE);
}
}
viewHolder.tv_like.setText(mStatus.attitudes_count + "");
viewHolder.tv_repost.setText(mStatus.reposts_count + "");
viewHolder.tv_comment.setText(mStatus.comments_count + "");
viewHolder.rly_content.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(mContext, CommentsActivity.class);
MyApplication.getInstance().setStatus(mStatus);
mContext.startActivity(intent);
}
});
viewHolder.roundImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(mContext, UserActivity.class);
MyApplication.getInstance().setUser(mStatus.user);
MyApplication.getInstance().setStatus(mStatus);
intent.putExtra("From", "MainActivity");
mContext.startActivity(intent);
}
});
viewHolder.lly_comments.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(mContext, PublishActivity.class);
MyApplication.getInstance().setStatus(mStatus);
intent.putExtra(Constants.KEY_TYPE, Constants.COMMENTS);
mContext.startActivity(intent);
}
});
viewHolder.lly_repost.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(mContext, PublishActivity.class);
MyApplication.getInstance().setStatus(mStatus);
intent.putExtra(Constants.KEY_TYPE, Constants.REPOST);
mContext.startActivity(intent);
}
});
viewHolder.iv_menu.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
mContext, R.layout.item_replay_comments);
arrayAdapter.add("收藏");
arrayAdapter.add("取消收藏");
ListView listView = new ListView(mContext);
listView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
listView.setDividerHeight(0);
listView.setAdapter(arrayAdapter);
final MaterialDialog dialog = new MaterialDialog(mContext)
.setTitle(mStatus.user.screen_name).setContentView(
listView);
dialog.setCanceledOnTouchOutside(true);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
switch (position) {
case 0:
long ids = Long.parseLong(mStatus.id);
mFavouritesAPI.create(ids, mListener);
break;
case 1:
long ids1 = Long.parseLong(mStatus.id);
mFavouritesAPI.destroy(ids1, mCancleListener);
break;
default:
break;
}
dialog.dismiss();
}
});
dialog.show();
}
});
return convertView;
}
class ViewHolder {
private RelativeLayout rly_content;
private LinearLayout lly_comments;
private LinearLayout lly_repost;
private RoundImageView roundImageView;
private TextView tv_name;
private TextView tv_time;
private TextView tv_content;
private TextView tv_reContent;
private TextView tv_like;
private TextView tv_repost;
private TextView tv_comment;
private View view_line;
private ImageView iv_menu;
private NineGridImageView nineGridView;
}
private RequestListener mListener = new RequestListener() {
@Override
public void onComplete(String response) {
// TODO Auto-generated method stub
if (!TextUtils.isEmpty(response)) {
FavoriteList list = FavoriteList.parse(response);
if (list != null) {
Toast.makeText(mContext, "收藏成功", Toast.LENGTH_LONG).show();
}
}
}
@Override
public void onWeiboException(WeiboException e) {
// TODO Auto-generated method stub
Toast.makeText(mContext, "你已经收藏过这条微博了", Toast.LENGTH_LONG).show();
}
};
private RequestListener mCancleListener = new RequestListener() {
@Override
public void onComplete(String response) {
// TODO Auto-generated method stub
if (!TextUtils.isEmpty(response)) {
FavoriteList list = FavoriteList.parse(response);
if (list != null) {
Toast.makeText(mContext, "取消收藏成功", Toast.LENGTH_LONG)
.show();
}
}
}
@Override
public void onWeiboException(WeiboException e) {
// TODO Auto-generated method stub
Toast.makeText(mContext, "你还未收藏过这条微博", Toast.LENGTH_LONG).show();
}
};
}
这样就可以展示微博的信息了,之后的评论列表,收藏列表等等都是类似的方法,只是调用的API不同而已。
整个app使用了很多开源库,包括九张图片库,颜色主图Prism框架等等。这次毕设最让我气愤的就是新浪限制第三方客户端的权限申请,很多API接口也已经无法拿到数据了。就这样。源码我也不给了,想要看完成代码的可以去搜索aisen微博,这个客户端功能更加完善,更加实用。git地址:https://github.com/wangdan/AisenWeiBo。