#Day03 #
#首页分页加载 ##
@Override
public ArrayList<AppInfo> onLoadMore() {
// 从网络加载数据
HomeProtocol protocol = new HomeProtocol();
ArrayList<AppInfo> moreList = protocol.getData(getListSize());// 加载下一页数据
return moreList;
}
----------------------
MyBaseAdapter.java
//获取集合大小
public int getListSize(){
return list.size();
}
断点调试 ##
Json解析过程断点调试
在HomeProtocol解析json的逻辑中,故意写错字段名
在HomeFragment的getData方法中打断点, 逐步跟踪到解析json的逻辑中
演示断点调试常用的一些功能和按钮
修复错误之后,再进行断点调试,查看效果
首页UI实现 ##
item布局
list_item_home.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="@drawable/list_item_bg_selector"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="3dp"
<ImageView
android:id="@+id/iv_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="8dp"
android:src="@drawable/ic_default" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/iv_icon"
android:singleLine="true"
android:text="京东"
android:textColor="#000"
android:textSize="18sp" />
<RatingBar
android:id="@+id/rb_star"
android:layout_width="wrap_content"
android:layout_height="16dp"
android:layout_below="@id/tv_name"
android:layout_marginTop="5dp"
android:layout_toRightOf="@id/iv_icon"
android:progressDrawable="@drawable/custom_ratingbar"
android:rating="5" />
<TextView
android:id="@+id/tv_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/rb_star"
android:layout_toRightOf="@id/iv_icon"
android:singleLine="true"
android:text="1000"
android:textColor="#9e9e9e"
android:textSize="15sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_download" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下载"
android:textColor="#000"
android:textSize="16sp" />
</LinearLayout>
</RelativeLayout>
<View
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="0.2dp"
android:background="#cccccc" />
<TextView
android:id="@+id/tv_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:singleLine="true"
android:text="京东是中国最大的巴拉巴拉巴拉巴拉巴拉巴拉拉巴拉拉巴拉拉拉巴拉拉巴拉拉不拉了"
android:textColor="#9e9e9e"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
自定义RatingBar
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@android:id/background"
android:drawable="@drawable/rating_small_empty">
</item>
<item
android:id="@android:id/secondaryProgress"
android:drawable="@drawable/rating_small_half">
</item>
<item
android:id="@android:id/progress"
android:drawable="@drawable/rating_small_full">
</item>
</layer-list>
更新首页item数据
HomeHolder.java
public class HomeHolder extends BaseHolder<AppInfo> {
private TextView tvName;
private ImageView ivIcon;
private TextView tvSize;
private TextView tvDesc;
private RatingBar rbStar;
private BitmapUtils mBitmapUtils;
@Override
public View initView() {
View view = View.inflate(UIUtils.getContext(), R.layout.list_item_home,
null);
tvName = (TextView) view.findViewById(R.id.tv_name);
ivIcon = (ImageView) view.findViewById(R.id.iv_icon);
tvSize = (TextView) view.findViewById(R.id.tv_size);
tvDesc = (TextView) view.findViewById(R.id.tv_desc);
rbStar = (RatingBar) view.findViewById(R.id.rb_star);
mBitmapUtils = BitmapHelper.getBitmapUtils();
mBitmapUtils.configDefaultLoadingImage(R.drawable.ic_default);
return view;
}
@Override
public void refreshView(AppInfo data) {
if (data != null) {
tvName.setText(data.name);
tvSize.setText(Formatter.formatFileSize(UIUtils.getContext(),
data.size));
tvDesc.setText(data.des);
rbStar.setRating((float) data.stars);
mBitmapUtils.display(ivIcon, HttpHelper.URL + "image?name="
data.iconUrl);
}
}
}
使用BitmapUtils加载图片
引入XUtils框架
BitmapUtils不是单例的,为了保证整个应用共用一个对象(避免多个对象造成内存溢出), 需要封装BitmapHelper,获取BitmapUtils对象.
BitmapHelper.java
/**
-
获取BitmapUtils对象, 保证多个模块共用一个BitmapUtils对象,避免内存溢出
* -
@author Kevin
*
*/
public class BitmapHelper {private static BitmapUtils mBitmapUtils = null; public static BitmapUtils getBitmapUtils() { if (mBitmapUtils == null) { mBitmapUtils = new BitmapUtils(UIUtils.getContext()); } //默认加载图片是一张空图 mBitmapUtils.configDefaultLoadingImage(R.drawable.nothing); return mBitmapUtils; } }
自定义ListView ##
去掉分割线
将主页面背景改成黑色,可以看见每个item之间有浅灰色的分割线
禁用Ratingbar的点击事件
Ratingbar默认可以点击,抢占了ListView的点击事件, 需要禁用掉Ratingbar的点击效果, 方法是设置属性isIndicator=true
去掉ListView默认的点击效果
去掉滚动过程中的黑色
/**
-
自定义listview
* -
@author Kevin
*
*/
public class MyListView extends ListView {public MyListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(); } public MyListView(Context context, AttributeSet attrs) { super(context, attrs); initView(); } public MyListView(Context context) { super(context); initView(); } private void initView() { this.setDivider(null);// 去掉item之间的分割线 this.setCacheColorHint(Color.TRANSPARENT);// 去掉滑动时偶现的黑色背景 this.setSelector(new ColorDrawable());// item自带的点击效果改为透明色,相当于去掉了默认点击的背景色 } }
应用页面实现 ##
AppFragment
逻辑和HomeFragment几乎一样
/**
-
应用
* -
@author Kevin
*
*/
public class AppFragment extends BaseFragment {ArrayList<AppInfo> mList = null; @Override public View onCreateSuccessView() { MyListView view = new MyListView(UIUtils.getContext()); view.setAdapter(new AppAdapter(mList)); return view; } @Override public ResultState onLoad() { AppProtocol protocol = new AppProtocol(); mList = protocol.getData(0); return check(mList); } class AppAdapter extends MyBaseAdapter<AppInfo> { public AppAdapter(ArrayList<AppInfo> list) { super(list); } @Override public BaseHolder<AppInfo> getHolder(int position) { return new AppHolder(); } @Override public ArrayList<AppInfo> onLoadMore() { AppProtocol protocol = new AppProtocol(); ArrayList<AppInfo> moreData = protocol.getData(getListSize()); return moreData; } }
}
/**
-
应用页访问网络
* -
@author Kevin
*
*/
public class AppProtocol extends BaseProtocol<ArrayList> {private ArrayList<AppInfo> mAppList;// 应用列表集合 @Override public String getKey() { return "app"; } @Override public String getParams() { return ""; } @Override public ArrayList<AppInfo> parseJson(String result) { try { JSONArray ja = new JSONArray(result); mAppList = new ArrayList<AppInfo>(); for (int i = 0; i < ja.length(); i++) { AppInfo info = new AppInfo(); JSONObject jo1 = (JSONObject) ja.get(i); info.des = jo1.getString("des"); info.downloadUrl = jo1.getString("downloadUrl"); info.iconUrl = jo1.getString("iconUrl"); info.id = jo1.getString("id"); info.name = jo1.getString("name"); info.packageName = jo1.getString("packageName"); info.size = jo1.getLong("size"); info.stars = jo1.getDouble("stars"); mAppList.add(info); } return mAppList; } catch (Exception e) { e.printStackTrace(); } return null; }
}
/**
-
应用页holder
* -
@author Kevin
*
*/
public class AppHolder extends BaseHolder {private TextView tvName; private ImageView ivIcon; private TextView tvSize; private TextView tvDesc; private RatingBar rbStar; private BitmapUtils mBitmapUtils; @Override public View initView() { View view = View.inflate(UIUtils.getContext(), R.layout.list_item_home, null); tvName = (TextView) view.findViewById(R.id.tv_name); ivIcon = (ImageView) view.findViewById(R.id.iv_icon); tvSize = (TextView) view.findViewById(R.id.tv_size); tvDesc = (TextView) view.findViewById(R.id.tv_desc); rbStar = (RatingBar) view.findViewById(R.id.rb_star); mBitmapUtils = BitmapHelper.getBitmapUtils(); mBitmapUtils.configDefaultLoadingImage(R.drawable.ic_default); return view; } @Override public void refreshView(AppInfo data) { if (data != null) { tvName.setText(data.name); tvSize.setText(Formatter.formatFileSize(UIUtils.getContext(), data.size)); tvDesc.setText(data.des); rbStar.setRating((float) data.stars); mBitmapUtils.display(ivIcon, HttpHelper.URL + "image?name="
data.iconUrl);
}
}
}
游戏页面简单实现 ##
/**
-
游戏
* -
@author Kevin
*
*/
public class GameFragment extends BaseFragment {@Override public View onCreateSuccessView() { TextView view = new TextView(UIUtils.getContext()); view.setText("GameFragment"); return view; } @Override public ResultState onLoad() { return ResultState.STATE_SUCCESS; }
}
专题页面 ##
/**
-
专题
* -
@author Kevin
*
*/
public class SubjectFragment extends BaseFragment {private ArrayList<SubjectInfo> mList; @Override public View onCreateSuccessView() { MyListView view = new MyListView(UIUtils.getContext()); view.setAdapter(new SubjectAdapter(mList)); return view; } @Override public ResultState onLoad() { SubjectProtocol protocol = new SubjectProtocol(); mList = protocol.getData(0); return check(mList); } class SubjectAdapter extends MyBaseAdapter<SubjectInfo> { public SubjectAdapter(ArrayList<SubjectInfo> list) { super(list); } @Override public BaseHolder<SubjectInfo> getHolder(int position) { return new SubjectHolder(); } @Override public ArrayList<SubjectInfo> onLoadMore() { SubjectProtocol protocol = new SubjectProtocol(); ArrayList<SubjectInfo> moreData = protocol.getData(getListSize()); return moreData; } }
}
/**
-
主题页访问网络
* -
@author Kevin
*
*/
public class SubjectProtocol extends BaseProtocol<ArrayList> {private ArrayList<SubjectInfo> mSubjectList;// 主题列表集合 @Override public String getKey() { return "subject"; } @Override public String getParams() { return ""; } @Override public ArrayList<SubjectInfo> parseJson(String result) { try { JSONArray ja = new JSONArray(result); mSubjectList = new ArrayList<SubjectInfo>(); for (int i = 0; i < ja.length(); i++) { SubjectInfo info = new SubjectInfo(); JSONObject jo1 = (JSONObject) ja.get(i); info.des = jo1.getString("des"); info.url = jo1.getString("url"); mSubjectList.add(info); } return mSubjectList; } catch (Exception e) { e.printStackTrace(); } return null; }
}
public class SubjectHolder extends BaseHolder {
private ImageView ivPic; private TextView tvDes; private BitmapUtils mBitmapUtils; @Override public View initView() { View view = View.inflate(UIUtils.getContext(), R.layout.list_item_subject, null); ivPic = (ImageView) view.findViewById(R.id.iv_pic); tvDes = (TextView) view.findViewById(R.id.tv_des); mBitmapUtils = BitmapHelper.getBitmapUtils(); mBitmapUtils.configDefaultLoadingImage(R.drawable.subject_default); return view; } @Override public void refreshView(SubjectInfo data) { if (data != null) { tvDes.setText(data.des); mBitmapUtils.display(ivPic, HttpHelper.URL + "image?name="
data.url);
}
}
}
----------------------------------------------
list_item_subject.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="@drawable/list_item_bg_selector"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_pic"
android:layout_width="match_parent"
android:layout_height="130dp"
android:padding="5dp"
android:scaleType="fitXY" />
<TextView
android:id="@+id/tv_des"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:singleLine="true"
android:text="哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈"
android:textColor="#000"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
按照指定比例展示宽高的自定义控件实现
为了让图片按照完美比例进行展现, 不被压缩, 需要自定义控件,该控件可以根据预设的比例来确定宽高
/**
-
按照比例展示宽高的自定义控件
* -
@author Kevin
*
*/
public class RatioLayout extends FrameLayout {private float ratio; public RatioLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public RatioLayout(Context context, AttributeSet attrs) { super(context, attrs); // 加载自定义属性的值 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RatioLayout); // 根据属性id获取属性值, 方式: R.styleable.名称_属性 ratio = typedArray.getFloat(R.styleable.RatioLayout_ratio, 0); // 回收TypedArray, 释放内存 typedArray.recycle(); } public RatioLayout(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); // MeasureSpec.EXACTLY 确定值, 比如把宽高值写死,或者match_parent // MeasureSpec.AT_MOST 至多, 能撑多大就多大, 类似wrap_content // MeasureSpec.UNSPECIFIED 未指定大小 if (widthMode == MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY && ratio != 0) { // 1. 根据布局宽度推算图片宽度 int imageWidth = widthSize - getPaddingLeft() - getPaddingRight(); // 2. 根据图片宽度和宽高比,推算图片高度 int imageHeight = (int) (imageWidth / ratio); // 3. 根据图片高度, 推算布局高度 heightSize = imageHeight + getPaddingTop() + getPaddingBottom(); // 4. 根据布局高度, 推算heightMeasureSpec heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
}
自定义属性
values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RatioLayout">
<attr name="ratio" format="float" />
</declare-styleable>
</resources>
推荐页面实现 ##
网络数据加载
/**
-
推荐页访问网络
* -
@author Kevin
*
*/
public class RecommendProtocol extends BaseProtocol<ArrayList> {private ArrayList<String> mRecommendList;// 推荐列表集合 @Override public String getKey() { return "recommend"; } @Override public String getParams() { return ""; } @Override public ArrayList<String> parseJson(String result) { try { JSONArray ja = new JSONArray(result); mRecommendList = new ArrayList<String>(); for (int i = 0; i < ja.length(); i++) { String str = ja.getString(i); mRecommendList.add(str); } return mRecommendList; } catch (Exception e) { e.printStackTrace(); } return null; } }
飞入飞出自定义控件
拷贝飞入飞出自定义控件的相关代码
StellarMap
/**
-
推荐
* -
@author Kevin
*
*/
public class RecommendFragment extends BaseFragment {private ArrayList<String> mList; @Override public View onCreateSuccessView() { // 初始化飞入飞出自定义控件 StellarMap stellar = new StellarMap(UIUtils.getContext()); // 设置内部文字距边缘边距为10dip int padding = UIUtils.dip2px(10); stellar.setInnerPadding(padding, padding, padding, padding); // 设置数据源 stellar.setAdapter(new RecommendAdapter()); // 设定展示规则,9行6列(具体以随机结果为准) stellar.setRegularity(6, 9); // 设置默认组为第0组 stellar.setGroup(0, true); return stellar; } @Override public ResultState onLoad() { RecommendProtocol protocol = new RecommendProtocol(); mList = protocol.getData(0);// 33条数据 return check(mList); } class RecommendAdapter implements StellarMap.Adapter { // 返回组的数量 @Override public int getGroupCount() { return 2; } // 每组某个组下返回孩子的个数 @Override public int getCount(int group) { int count = mList.size() / getGroupCount();// 用总数除以组个数就是每组应该展示的孩子的个数 if (group == getGroupCount() - 1) {// 由于上一行代码不一定整除, 最后一组,将余数补上 count += mList.size() % getGroupCount(); } return count; } @Override public View getView(int group, int position, View convertView) { if (group > 0) {// 如果发现不是第一组,需要更新position, 要加上之前几页的总数,才是当前组的准确位置 position = position + getCount(group - 1) * group; } TextView view = new TextView(UIUtils.getContext()); view.setText(mList.get(position)); // 设置随机文字大小 Random random = new Random(); int size = 16 + random.nextInt(10);// 产生16-25的随机数 view.setTextSize(TypedValue.COMPLEX_UNIT_SP, size);// 以sp为单位设置文字大小 // 设置随机文字颜色 int r = 30 + random.nextInt(210);// 产生30-239的随机颜色, 绕过0-29, // 240-255的值,避免颜色过暗或者过亮 int g = 30 + random.nextInt(210); int b = 30 + random.nextInt(210); view.setTextColor(Color.rgb(r, g, b)); return view; } @Override public int getNextGroupOnZoom(int group, boolean isZoomIn) { if (!isZoomIn) { // 下一组 if (group < getGroupCount() - 1) { return ++group; } else { return 0;// 如果没有下一页了,就跳到第一组 } } else { // 上一组 if (group > 0) { return --group; } else { return getGroupCount() - 1;// 如果没有上一页了,就跳到最后一组 } } } }
}
排行模块实现 ##
网络数据加载
和推荐模块类型
/**
-
排行页访问网络
* -
@author Kevin
*
*/
public class HotProtocol extends BaseProtocol<ArrayList> {private ArrayList<String> mHotList;// 推荐列表集合 @Override public String getKey() { return "hot"; } @Override public String getParams() { return ""; } @Override public ArrayList<String> parseJson(String result) { try { JSONArray ja = new JSONArray(result); mHotList = new ArrayList<String>(); for (int i = 0; i < ja.length(); i++) { String str = ja.getString(i); mHotList.add(str); } return mHotList; } catch (Exception e) { e.printStackTrace(); } return null; }
}
HotFragment
/**
-
排行
* -
@author Kevin
*
*/
public class HotFragment extends BaseFragment {private ArrayList<String> mList; @Override public View onCreateSuccessView() { int padding = UIUtils.dip2px(10); // 为了使布局可以上下滑动,需要用ScrollView包装起来 ScrollView scrollView = new ScrollView(UIUtils.getContext()); // 设置ScrollView边距 scrollView.setPadding(padding, padding, padding, padding); // 初始化自定义控件 FlowLayout flow = new FlowLayout(UIUtils.getContext()); // 水平间距 flow.setHorizontalSpacing(UIUtils.dip2px(6)); // 竖直间距 flow.setVerticalSpacing(UIUtils.dip2px(8)); // 根据接口返回的数据个数,动态添加TextView for (final String str : mList) { TextView view = new TextView(UIUtils.getContext()); view.setText(str); view.setTextColor(Color.WHITE); view.setGravity(Gravity.CENTER); view.setPadding(padding, padding, padding, padding); view.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); // 设置随机文字颜色 Random random = new Random(); int r = 30 + random.nextInt(210); int g = 30 + random.nextInt(210); int b = 30 + random.nextInt(210); int color = 0xffcecece;// 按下后偏白的背景色 // 根据默认颜色和按下颜色, 生成圆角矩形的状态选择器 Drawable selector = DrawableUtils.getStateListDrawable( Color.rgb(r, g, b), color, UIUtils.dip2px(6)); // 给TextView设置背景 view.setBackgroundDrawable(selector); // 必须设置点击事件, TextView按下后颜色才会变化 view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(UIUtils.getContext(), str, Toast.LENGTH_SHORT).show(); } }); // 给自定义控件添加view对象 flow.addView(view); } scrollView.addView(flow); return scrollView; } @Override public ResultState onLoad() { HotProtocol protocol = new HotProtocol(); mList = protocol.getData(0); return check(mList); }
}
创建圆角矩形对象
/**
-
生成图像的工具类
* -
@author Kevin
*/
public class DrawableUtils {/**
-
创建圆角矩形
* -
@param rgb
-
颜色值
-
@param radius
-
圆角半径
-
@return
*/
public static Drawable getGradientDrawable(int rgb, int radius) {
// 初始化对象
GradientDrawable drawable = new GradientDrawable();
// 矩形类型
drawable.setGradientType(GradientDrawable.RECTANGLE);
// 设置颜色
drawable.setColor(rgb);
// 设置圆角半径
drawable.setCornerRadius(radius);
return drawable;
}/**
-
返回状态选择器对象(selector)
* -
@param normal
-
默认图像
-
@param pressed
-
按下图像
*/
public static Drawable getStateListDrawable(Drawable normal,
Drawable pressed) {
StateListDrawable drawable = new StateListDrawable();
drawable.addState(new int[] { android.R.attr.state_pressed }, pressed);
drawable.addState(new int[] {}, normal);
return drawable;
}
/**
- 返回状态选择器对象(selector)
* - @param normalColor
-
默认颜色
- @param pressedColor
-
按下颜色
- @param radius
-
圆角半径
- @return
*/
public static Drawable getStateListDrawable(int normalColor,
int pressedColor, int radius) {
Drawable normal = getGradientDrawable(normalColor, radius);
Drawable pressed = getGradientDrawable(pressedColor, radius);
return getStateListDrawable(normal, pressed);
}
}