GooglePlay Day03

Day03 01.昨天内容总结 ##

Day03 02.分页加载网络数据 ##

首页分页加载 ##

把第一页的数据加载出来之后 希望能够'加载更多'数据 我们在HomeFragment的onLoadMore方法中 直接返回了一个null 所以就显示加载更多失败 那如果还是要加载更多的话 还是要用到我们的网络了 



HomeFragment.java



@Override

public ArrayList<AppInfo> onLoadMore() {

	// 从网络加载数据

	HomeProtocol protocol = new HomeProtocol();

	//当前集合有多少个元素 index就写多少

	ArrayList<AppInfo> moreData = protocol.getData(getListSize());// 加载下一页数据

	return moreData;

}



注意:

我们第一次进来的时候在onNetLoad中getData(index) index 给设了0  

但是在加载更多的方法onLoadMore中 index肯定变了 不是加载第一页了 

从0开始是第一页 20开始是第二页 40开始是第三页 所以说这个地方怎么去写呢 



我们第一页数据加载完了之后 那ListView是20条数据 而当我们第二页数据加载完了之后

总的数据是40 所以现在20 40 刚刚好等于目前来讲的集合的数量 

所以在这个地方传什么 就看当前集合是多少个 我们就传几  即当前集合有多少个元素 index就是多少

这样就不用自己维护个变量 每次加等于20 加等于20 那样很麻烦



那我们现在要拿到集合的数量 集合现在在哪里 在我们的 基类MyBaseAdapter中有个list



它就是我们的集合 那现在要取集合的数量 那我们就再给这个类暴露一个方法 来返回我们集合的数量



即在MyBaseAdapter中再写一个方法 getListSize



-----------------------------------------------------------

MyBaseAdapter.java



//获取获取当前集合大小

public int getListSize(){

	return list.size();

}

-----------------------------------------------------------

断点调试 ##

Json解析过程断点调试

在HomeProtocol解析json的逻辑中,故意写错字段名
在HomeFragment的getData方法中打断点, 逐步跟踪到解析json的逻辑中
演示断点调试常用的一些功能和按钮
修复错误之后,再进行断点调试,查看效果

Day03 03.首页item布局文件开发&自定义Ratingbar ##

首页UI实现 ##

ListView每个item卡片的效果怎么去搞呢 



它本身就有一个卡片的背景  在讲智慧北京的时候 那个组图模块 就是一张一张的卡片

然后给它设置一个margin 让它有一个外边距 就能隔开

我们在这里 用了一个状态选择器 好区分按下和弹起的效果 即list_item_bg_selector.xml



所以现在要把原来的布局文件 list_item_home.xml布局文件更新一下 

可以直接将布局文件拷贝过来 

ListView的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>

--------------------------------------------------------------



item的背景图片选择器 (按下和抬起图片)



list_item_bg_selector.xml



<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">



<item android:drawable="@drawable/list_item_bg_pressed" android:state_pressed="true"></item>

<item android:drawable="@drawable/list_item_bg_normal"/>



</selector>



-----------------------------------------------------------------



评分的小星星在讲自定义的时候 提到过 

即android原生自带的RatingBar 但那个星星 它是一个大星星 而这个地方的星星地小星星

所以那就是说要基于android自带的大星星 定义成我们的小星星 



具体怎么做呢



即基于星星的本质 它继承的AbsSeekBar 而SeekBar又继承的是ProgressBar 

星星本事就是进度条的这样一个样式 

在RatingBar布局处添加progressDrawable的状态选择器为custom_ratingbar.xml 

就可以定义它当前是什么类型 	



自定义小星星 那它的图片 已经在在图片中复制过来了 空星星 半颗星星 实心星星 三张图片



在custom_ratingbar.xml 中 将空星星作为背景 将半颗星星作为缓冲 最后才是我们的实心星星

在RatingBar布局添加android:rating= 5 它就显示五颗实心 1.5就是一颗半实心 1.9就是两颗实心

即四舍五入 



手机卫士杀毒时 自定义了一个进度条 这个地方的用法和那个地方完全一样 只不过表现的出来的效果不一样

自定义RatingBar

custom_ratingbar.xml



	<?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>



------------------------------------------------------------



上边布局完成之后 就可以在HomeHolder中加载布局了 

更新首页item数据

	HomeHolder.java



	public class HomeHolder extends BaseHolder<AppInfo> {



		private ImageView ivIcon;	//图标

		private TextView tvName;	//名称

		private RatingBar rbStar;	//评分星

		private TextView tvSize;    //安装包的大小

		private TextView tvDesc;    //描述



		private BitmapUtils mBitmapUtils; 

	

		@Override

		public View initView() {

			View view = View.inflate(UIUtils.getContext(), R.layout.list_item_home,

					null);



			ivIcon = (ImageView) view.findViewById(R.id.iv_icon); //应用图标

			tvName = (TextView) view.findViewById(R.id.tv_name);  //应用名称

			rbStar = (RatingBar) view.findViewById(R.id.rb_star); //评分星

			tvSize = (TextView) view.findViewById(R.id.tv_size);  //应用安装包的大小

			tvDesc = (TextView) view.findViewById(R.id.tv_desc);  //应用描述

			

	 		//new一个bitmapUtils

			mBitmapUtils = new BitmapUtils(UIUtils.getContext());

			mBitmapUtils.configDefaultLoadingImage(R.drawable.nothing);

			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);
 }
 }
 }

注意: 在refreshView中 根据参数 AppInfo data 拿到封装的数据 然后将数据填充给每个控件

判空是为了避免出现空指针异常 



tvSize应用安装包的大小  data.size是long类型的 我们希望转成多少多少MB 即带单位的这种类型

手机卫士中讲过一个类Formatter 它里边有个类 formatFileSize 用它即可



ivIcon应用图标 它是一个图片 我们要动态的从网上下载下来 这时候 就可以用BitmapUtils了



要用BitmapUtils 首先要引入XUtils包 然后就可以使用BitmapUtils了 



在initView中new一个bitmapUtils把bitmapUtils传进来 然后就可以在refreshView中使用



它的uri是什么样子呢 看服务器代码 中的 首页json数据 中的iconUrl 即 

"iconUrl": "app/com.youyuan.yyhl/icon.jpg" 而这个不是一个完整的链接

它是在主域名的后边跟的这个链接 



所以先通过HttpHelper把主域名拿到手 即 HttpHelper.URL



后边的跟的东西是服务器规定的这样写 即 "image?name="

它是说你将来要下载图片的话 image的name等于啥 就等于传过来的app/com.youyuan.yyhl/icon.jpg这个东西 你就可以吧图片下载下来 由服务器规定 每个服务器的规定可能都不太一样 



然后在加上 data.iconUrl



bitmapUtils 在智慧北京中经常用它 每次都是去new一个bitmapUtils 

bitmapUtils底层也是采用了三级缓存的机制 而且它也用到了LruCache这样一个东西

而LruCache能够定义内存的一个上线 比如说上线是个2MB 那这时候就不能超过2MB

超过了就会把之前的早的东西给删除掉 这是bitmapUtils的一个原理



但是有个小问题 如果我new一个bitmapUtils 它是不是就会创建一个LruCache

那如果我将来在别的地方也new一个bitmapUtils的话 它是不是又创建一个LruCache

那我如果创建足够多的bitmapUtils对象的话 就会有足够多的LruCache

而每个LruCache的上线都是2MB 那加起来10个LruCache 那就是20MB 早都内存溢出了



但是我们在讲智慧北京的时候没有考虑到这一点  我们只是纯纯脆脆的就用bitmapUtils

要用的话就new一个 要用的话就new 一个 其实你是在底层 是new了一个又一个的LruCache



那这样的你是并没有解决内存溢出的的问题 我们所说的2MB 是所有的的加起来2MB

而你现在只是这个页面2MB 那个页面2MB 那几个页面一切就奔溃了



当时智慧北京中没有奔溃的话 是因为bitmapUtils它还比较强大 还能hold住

但是你使劲滑滑滑的话有可能也会崩溃



所以这个bitmapUtils我们不能够用它去new一个 而应该把它搞成一个对象 

也就是类似于一个单例的方式  那这时候我们就写一个bitmapUtils这样一个对象去获取



 写一个类叫做BitmapHelper.java



---------------------------------------------------------------

Day03 04.首页界面展现 ##

使用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) {
    
      				
    
      				synchronized (BitmapHelper.class) {
    
      					if (mBitmapUtils == null ) {
    
      						
    
      						mBitmapUtils = new BitmapUtils(UIUtils.getContext());
    
      					}
    
      				}
    
      			}
    
      			return mBitmapUtils;
    
      		}
    
      	}
    

    注意:

    这就是一个单例 当然它会有线程安全的问题 看你考虑不考虑 方便不方便

    要考虑的话 那就在判断它等于空的下边 搞一个synchronized关键词 把它包起来

    然后再判断 如果mBitmapUtils == null 即还的这样判断一次

    这就是一个单例模式 而且填上了线程安全的单例模式

    然后再回到我们的HomeHolder中 要去搞bitmapUtils的话

    就要用单例去搞了 就要用BitMapHelper


    HomeHolder.java

    @Override

      	public View initView() {
    
    
    
      		  //new一个bitmapUtils
    
      		 //mBitmapUtils = new BitmapUtils(UIUtils.getContext());
    
    
    
      		   //获取单例的bitmapUtils 保证多个模块共用一个BitmapUtils对象,避免多个BitmapUtils对象造成内存溢出
    
      		   mBitmapUtils = BitmapHelper.getBitmapUtils();
    
    
    
      		   mBitmapUtils.configDefaultLoadingImage(R.drawable.ic_default);
    

    }


Day03 05.ListView控件封装 ##

自定义ListView ##

禁用Ratingbar的点击事件
 我们给每个item设置了状态选择器 点击的时候应该变成灰色 但是现在颜色也不变
 因为Ratingbar在这搞鬼呢

即Ratingbar现在是属于item的孩子 而Ratingbar它本身是可以被点击的(勾选)

所以说它是由事件的处理的 它就把这个点击事件给拦截掉了 让它的爹item拿不到点击事件



而Ratingbar在这里肯定不能让他可以手动的勾选 而是应该由后台决定 是几颗星就是几颗星

所以说Ratingbar在这不需要点击事件 只是需要展示就行 



那就在Ratingbar的布局中设置一个属性 android:isIndicator="true" 即可禁止掉它的点击事件

isIndicator就是说它是一个标识 并不具备点击的操作 只是一个指示的东西

这样它的爹item就能获得点击事件 

	

Ratingbar默认可以点击,抢占了ListView的点击事件, 需要禁用掉Ratingbar的点击效果, 方法是设置属性isIndicator=true

去掉分割线
 即每个item之间 ListView默认会有一条很细的线 现在看不太清楚 
 将主页面背景改成黑色,就可以看见每个item之间有浅灰色的分割线

setDivider(null); 

去掉ListView默认的点击效果
 但是还有问题
 即 item可以点击了 但是点击的时候 它的周围还有一圈蓝色的线 这个蓝色是listView默认的颜色
 所以需要把listview颜色背景色的点击的效果去掉
 setSelector(new ColorDrawable())

去掉滑动时偶现的黑色背景
 智慧北京时我们遇到一个问题 就是有时候滑动listView时 突然背景屏就变成黑色了

在某种情况下它会出现 那我们干脆就直接在这处理了 

setCacheColorHint(Color.TRANSPARENT) 即改为透明色



-----------------------------------------------------------------



现在在搞listview的时候 我们是直接在HomeFragment中的onCreateSuccessView方法

中new了一个listview 我们是希望以后只要用到listview 它都没有这种点击的蓝色效果



包括我们还会加一些别的属性 

那这时候我们就可以对这个listview控件进行一下封装 



MyListView.java



	/**
  • 自定义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();
    
      	}
    
      
    
      	//初始化listview
    
      	private void initView() {
    
      		
    
      		// 去掉item之间的分割线
    
      		this.setDivider(null);
    
    
    
      		// 去掉滑动时偶现的黑色背景 改为透明色
    
      		this.setCacheColorHint(Color.TRANSPARENT);
    
    
    
      		// item自带的点击效果改为透明色,相当于去掉了默认点击的蓝色背景 不能传null 否则空指针
    
      		this.setSelector(new ColorDrawable());
    
      	}
    
      }
    

    写完initView方法之后 把该方法加到三个构造方法中

    这样以后不管是哪个页面用到ListView 都会调用initview方法


    封装完MyListView之后 那原来在HomeFragment中用的话就不在用以前的listview

    所以注释掉 改用MyListView

    HomeFragment.java

    @Override

    public View onCreateSuccessView() {

      //ListView view = new ListView(UIUtils.getContext());
    
      MyListView view = new MyListView(UIUtils.getContext());
    
    
    
      //initData();
    
      view.setAdapter(new HomeAdapter(data));
    
      return view;
    

    }


Day03 06.应用模块开发(类似首页) ##

应用页面实现 ##

逻辑和HomeFragment完全一样

AppFragment.java

即将HomeFragment中的 HomeAdapter改为AppAdapter 

将getHolder方法中return 的HomeHolder改为AppHolder 

并复制HomeHolder改名为AppHolder即可



将onLoadMore方法中的 HomeProtocol 改为 AppProtocol

并复制 HomeProtocol 改名为 AppProtocol 将里面的 mPictureList 

即首页轮播图的json解析删除掉即可



/**
  • 应用
     *

  • @author Kevin
     * 
     */
     public class AppFragment extends BaseFragment {

      ArrayList<AppInfo> data;
    
    
    
      @Override
    
      public View onCreateSuccessView() {
    
      	MyListView view = new MyListView(UIUtils.getContext());
    
      	view.setAdapter(new AppAdapter(data));
    
      	return view;
    
      }
    
    
    
      @Override
    
      public ResultState onLoad() {
    
      	AppProtocol protocol = new AppProtocol();
    
      	data = protocol.getData(0);
    
      	return check(data);
    
      }
    
    
    
      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;
    
      	}
    
    
    
      }
    

    }


    AppProtocol.java

    比HomeProtocol.java 少的就是 mPictureList 即首页轮播图的json解析

    /**

  • 应用页访问网络
     *

  • @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;
    
      }
    

    }


    AppHolder和HomeHolder一模一样 复制改名为AppHolder即可

    AppHolder.java

    /**

  • 应用页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);
 }
 }
 }

----------------------------------------------------------



应用页面逻辑和首页页面完全一样 记录下逻辑的思路 以便更好的理解整个项目



在Fragment包下找到AppFragment点击打开,其实和HomeFragment的逻辑一样 

1.在AppFragment中的onCreateSuccessView方法new一个MyListView对象 然后把这个view返回回去



2那MyListView需要一个Adapter 在AppFragment中写一个AppAdapter

范型的类型一样 都是AppInfo  AppAdapter中重写一下MyBaseAdapter的方法 

主需要重写两个方法  getHolder 和onLoadMore  

getHolder的返回值BaseHolder里边其实它传的是一个范型 也就是我们的AppInfo

然后在AppFragment的onCreateSuccessView方法中就可以给MyListView设置Adapter 需要传一个list 现在没有 在onNetLoad加载的时候才有



3在onNetLoad方法中加载网络数据  

要加载的话 

4首先需要再写一个网络加载的封装 即AppProtocol 	

继承的是我们的BaseProtocol 数据类型和HomeProtocol一样

是ArrayList<AppInfo> 

这个类型是指数据解析完之后它返回的什么类型 它肯定返回的是一个集合

重写三个方法 getKey getParams parseJson 

查看服务器代码知道 AppServlet的key是app 同样在getKey中返回

getParams同样返回空串



打开应用页面的json数据 即applist1~3



可以看到它的跟标签就是一个数组 里边再是一个一个的大括号(即一个一个的Object即对象) 和首页的Object是一样的 



跟标签就是一个Array的话 那我们在parseJson方法中

首先就要new一个JsonArray 并把result参数传过来即

	JSONArray ja = new JSONArray(result);(要是不把这个result告诉人家 人家知道解析什么)

然后搞一个for循环去解析

	通过ja拿到一个JSONObject

	具体从JSONObject中拿到一个一个的字段的信息 可以从HomeProtocol中复制一下



这就是我们的解析 把它都塞到info中 

然后在写一个集合(ArrayList<AppInfo>) 把info都add进去

	然后把这个list return 回去



AppProtocol搞定后 

就可以回到AppFragment中的onNetLoad方法中加载网络数据	

	new一个AppProtocol对象 通过它加载第一页数据即.getData(0)

	返回的是一个集合类型的data return的话 要根据实际情况去判断 即:

		return check(data);



然后再在AppFragment的getHolder方法中实现我们的界面展示 

界面展示的话需要一个Holder 

那就再创建一个AppHolder 和HomeHolder完全一样 复制改名即可



在getHolder中 return new AppHolder即可



再在加载更多的方法onLoadMore中new一个AppProtocol对象



  通过它加载更多数据即.getData(getListSize())

  返回的是一个集合类型的moreData

  然后再return回去



这样应用页面的逻辑也就完成  运行程序查看即可

-----------------------------------------------------------

Day03 07.游戏模块(不实现) ##

游戏页面简单实现 ##

游戏页面想实现的话 数据在服务器中 即 gamelist1~3 



数据格式和我们的首页和应用都一样 不过里边的内容全都是游戏 



区别显示那些内容 主要的一个区别是key的不同 即首页的key是home 应用的key是app



/**
  • 游戏
     *

  • @author Kevin
     * 
     */
     public class GameFragment extends BaseFragment {

      @Override
    
      public View onCreateSuccessView() {
    
      	TextView view = new TextView(UIUtils.getContext());
    
      	//view.setText("GameFragment");
    
      	view.setText(this.getClass().getSimpleName()); //当前类名是什么就显示什么文字
    
      	return view;
    
      }
    
    
    
      @Override
    
      public ResultState onLoad() {
    
      	return ResultState.LOAD_SUCCESS;
    
      }
    

    }

Day03 08.专题模块开发 ##

专题页面 ##

查看专题的布局 和我们的组图的布局一模一样 



要搞得话 首先先去加载专题的 网络数据显示出来 然后再弄布局 



专题的网络数据在哪里呢 服务器中没有专题的json数据 

所以我们把专题的json数据全都写死在代码中了 全都写死在服务器中了

即打开serser下的servlet包下的 SubjectServlet.java



strResponse1~4代表分页 即第一页第二页第三页第四页 



第一页的数据 复制到hijson工具中



专题的json数据特别简单 就是一个des(描述)和url()



------------------------------------------------------------------

1.我们先写网络数据	



/**
  • 主题页访问网络
     *

  • @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);//或写成JSONObject jo1 = ja.getJSONObject(i);
    
      			info.des = jo1.getString("des");
    
      			info.url = jo1.getString("url");
    
    
    
      			mSubjectList.add(info);
    
      		}
    
    
    
      		return mSubjectList;
    
    
    
      	} catch (Exception e) {
    
      		e.printStackTrace();
    
      	}
    
      	return null;
    
      }
    

    }

    注意:

      parseJson时 要用hijson工具查看下json数据 它的最外边是一个数组 即[]
    
      所以首先需要new一个JSONArray 并把result参数放进来
    
      再用hijson工具查看下json数据 它的里边是一个一个的对象 即{},{}
    
      所以然后写一个for循环 来解析json数据 
    
      用JSONArray去获取一个JSONOject 即 ja.getJSONObject();
    
      获取到JSONObject后 我们可以从JSONObject中获取到一个des 和一个url
    
      并封装到对象中 即new一个SubjectInfo对象 
    
      然后封装到对象中 即info.des = jo1.getString("des");
    
      最后我们要把封装好的对象放在集合中 
    
      所以new一个ArrayList<SubjectInfo>
    
      把封装好的对象放在集合中 即 mSubjectList.add(info);
    
    
    
      刚建好SubjectProtocol.java并继承了BaseProtocol时,
    
      <T>报错是因为需要一个具体的类型 类型的话 我们现在是一个专题的这样一个对象 即
    
      {
    
      "des": "一周新锐游戏精选",
    
      "url": "image/recommend_01.jpg"
    
      }
    
      
    
      那我们就把它封装成这样一个对象吧 即 SubjectInfo.java
    
    
    
      SubjectInfo很简单 就两个字段 des url
    

    SubjectInfo.java

    /**
    

专题数据封装
@author Brant
 *
 */
 public class SubjectInfo {

		public String url;

		

		public String des;

	

	}



-------------------------------------------------

上边主题相关的一些信息解析完之后 就可以到主题的Fragment中去加载数据了



SubjectFragment.java



/**
  • 专题
     *

  • @author Kevin
     * 
     */
     public class SubjectFragment extends BaseFragment {

      private ArrayList<SubjectInfo> data;
    
    
    
      @Override
    
      public View onCreateSuccessView() {
    
      	MyListView view = new MyListView(UIUtils.getContext());
    
      	view.setAdapter(new SubjectAdapter(data));
    
      	return view;
    
      }
    
    
    
      @Override
    
      public ResultState onNetLoad() {
    
      	SubjectProtocol protocol = new SubjectProtocol();
    
      	data = protocol.getData(0);
    
      	return check(data);
    
      }
    
    
    
      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;
    
      	}
    
      }
    

    }

    在onNetLoad方法中加载数据 首先new一个SubjectProtocol

    然后getData(0) 获取第一页数据 返回一个data到数组Arraylist

    然后返回一个check(data)

    上边就是我们的这样一个数据 当然它是一个ListView 所以在onCreateSuccessView

    方法中再new一个MyListView 然后需要给它设置一个Adapter

    所以再写一个SubjectAdapter 继承自MyBaseAdapter时 需要返回一个对象 每个item的对象都是SubjectInfo

    getHolder处返回的BaseHolder报黄色警告 所以我们可以给它范型

    onLoadMore处再new一个SubjectProtocol

    然后getData(getListSize) 获取其他页数据 返回一个moreData类型的ArrayList 然后返回一个moreData

    在getHolder处 我们还需要一个holder 所以返回一个 new SubjectHolder();

    并在holder包下建一个SubjectHolder.java


    SubjectHolder.java

    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);
 }
 }
 }

在initView方法中需要加载它的布局文件 所以再新建一个布局文件 list_item_subject.xml



----------------------------------------------



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="centerCrop" />



        <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>



为了能够让图片撑开这个屏幕的话 就给它设置一个 android:scaleType="fitXY"

当然这时候图片可能被拉伸 所以给一个centerCrop值 图片可以裁剪 



-----------------------------------------------------------

Day03 09.自定义属性 ##

专题页面写完了 但是图片的展示 我们是把高度写死的 宽度除了一个margin之外基本是填充屏幕的 写死了之后会有一个问题 首先我们为了让图片填充屏幕 

 可以给imageView的scaleType设置一个fixXY 让它撑起来 但是撑起来之后 会导致图片拉伸或变形 这时候我们就可以给scaleType设置一个centerCrop来裁剪 

用它来裁剪图片虽然不变形 但是它不完整 

那如何保证图片是完整的 而且高度和它自己原来的比例的一样完美的去展现  



实际上计算图片的宽高比例是 用图片的高除以宽即可  比如 444/183=2.43



那我们这样 宽度填充屏幕的matchParent 这样没啥说的 在宽度的基础上 我们除以一个2.43 

是不是就可以把它的高度去计算下来? 但是又由于android每个屏幕的宽度都不一样

所以你无法完整的去确定宽度等于多少 所以我们应该动态的去拿到它的宽度



拿到之后再去除以2.43 把它的高度计算下来 

我们不要把高度写死 即把高度动态的随着它的宽度去计算下来 



这时候我们就可以去写一个自定义的view 那么自定义的view怎么去写 是个什么道理呢



我们自定义一个可按照比例来调整它的高度的这样一个布局(它是一个容器) 让它继承FrameLayout这样的一个容器 而它的宽高度 就按照我们指定的宽高度比例来展示它的

宽度和高度

然后再把ImageView填充给这个布局  这样的话我们外边的宽高是多少比例

ImageView也是多少比例 

Day03 10.自定义RatioLayout控件 ##
Day03 11.自定义控件RationLayout02 ##

按照指定比例展示宽高的自定义控件实现

为了让图片按照完美比例进行展现, 不被压缩, 需要自定义控件,该控件可以根据预设的比例来确定宽高

RatioLayout.java



/**
  • 按照比例展示宽高的自定义控件
     *

  • @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);
    
      	// 加载自定义属性的值2.43
    
      	TypedArray typedArray = context.obtainStyledAttributes(attrs,
    
      			R.styleable.RatioLayout);
    
      	// 根据属性id获取属性值, 方式: R.styleable.自定义属性名称_具体属性
    
      	ratio = typedArray.getFloat(R.styleable.RatioLayout_ratio, 0);
    
      	// 回收TypedArray, 释放内存
    
      	typedArray.recycle();
    
      	System.out.println("ratio:" + ratio);//测试ratio是否取到
    
    
    
      }
    
    
    
      public RatioLayout(Context context) {
    
      	super(context);
    
      }
    
    
    
      @Override
    
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
      	
    
      	int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    
      	int heightMode = MeasureSpec.getMod(heightMeasureSpec);
    
    
    
      	int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    
      	int heightSize = MeasureSpec.getSiz(heightMeasureSpec);
    
    
    
      	// MeasureSpec.EXACTLY 确定值, 比如把宽高值写死,或者match_parent
    
      	// MeasureSpec.AT_MOST 至多, 能撑多大就多大, 类似wrap_content
    
      	// MeasureSpec.UNSPECIFIED 未指定大小
    
      	//宽度必须已经确定,高度没有确定,ratio比例一定要大于0
    
      	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 = MeasureSpec.makeMeasureSpec(heightSize,
    
      				MeasureSpec.EXACTLY);
    
      	}
    
      		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
      }
    

    }

    1.按比例设定宽高 那就需要把比例传给你 这样去写 拷贝RatioLayout的全路径

    把ImageView塞到这个布局中来

    4.将list_item_subject.xmld

    中自定义属性itcast:ratio="2.43"的值2.43传递过来

    可以从参数attrs中拿到这个属性

    即attrs.getAttributeFloatValue(namespace,attribute,defaultValue)

    在这个地方 教大家一个新的方法 在看一些开源的代码时 你发现他们拿的方式比较奇特

    怎么去拿的呢 context.obtainStyledAttributes(attrs,attrs);

    它里边需要传两个值 AttributeSet 和int[]

    AttributeSet就是我们构造方法的参数AttributeSet 那后边这个int型数组是啥呢

    我们这个属性 因为它有很多种 我们现在是不是只指定了一个

    它有可能是很多个 它底层就是把这个属性 放在 int数组中维护起来

    那我们现在就要搞一个数组 而这个数组是在R文件中去生成的 R.styleable.RatioLayout

    意思是你在attrs文件中定义了一个RatioLayout的这样一个属性 那这时候系统就会把它生成一个R文件 其实我们可以看下R文件(在gen/com.itcast.googleplay文件夹下) 就有一个RatioLayout

    拿到它之后 返回一个TypeArray的这样类型的一个数组 我们在typedArray中就可以拿到我们的某一个对象 即typedArray.getFloat(index, defValue)

    这时候要把数组的位置传过来 即这时候要传个index 这时候是一堆属性 我们要找准这个属性 也是可以用R文件找到的

    即R.styleable.RatioLayout_ratio

    defValue 设置成0

    RatioLayout_ratio 这个id怎么去命名呢 这个id其实就是我们的自定义属性名_具体属性 这个是系统自己生成的 如果在多加一个具体属性

    即 那么R文件也会自动生成

    RatioLayout_ratio2

    返回一个float类型的ratio之后 obtainStyledAttributes报黄色警告

    原因是 用完之后需要回收一下 即typedArray.recycle();

    5.有了自定义属性之后 我们就可以根据比例去修改我们这个布局的尺寸了

    要修改尺寸 我们再回顾下之前说的 绘一个东西我们会这样去做

    measure --> layout --> draw 对应于它们 分别有onmeasure onlayout ondraw

    修改它的尺寸是不是onMeature啊 我们重写一个方法 onMeasure

    这是尺寸的一个回调 而在尺寸的回调里面 我们能拿到

    参数 widthMeasureSpec heightMeasureSpec

    它们其实并不是它的宽度 高度 它是宽度里边还封装了它的类型 即它们是怎样的一个适配效果

    MeasureSpec.getSize() 把这个宽widthMeasureSpec 高heightMeasureSpec传过来 就可以把这个widthSize heightSize获取出来

    还可以获取它布局的类型 MeasureSpec.getMode 把这个宽widthMeasureSpec 高heightMeasureSpec 传过来 就可以把这个widthMode heightMode 获取出来

    它有MeasureSpec.EXACTLY确定的宽高 类似布局文件中宽高写死成dp,获取match_parent

    MeasureSpec.AT_MOST 至多的类型 能多大就多大 相当于Wrap_content

    MeasureSpec.UNSPECIFIED未定义宽高

    这三种模式

    有时候你会发现 你用一个大的scrollView去加载一些布局 其实scrollView的高度它是无法确定的 只有你把所有孩子的高度确定出来后 scrollView的高度才能确定出来 它的高度是无法确定的 即使scrollView你的高度写成matchParent wrapContent 还是认为它不确定 直到所有的孩子确定出来 它才能确定 所以这个就是指的未定义宽高

    7.我们首先要做个判断 根据它的宽度来确定高度 而这个宽度必须是一个确定的值 如果你连宽度都不确定的话 那高度就更没法确定

    所以我们宽度的模式 EXACTLY是确定的 即如果widthMode == EXACTLY 那我们的宽度就是确定的 这时候我们才去计算 宽度没有确定的话 根本没有必要去写

    同时的话 我们的高度的模式呢 不确定 即&& heightMode != EXACTLY(不确定分两种 AT_MOST和UNSPECIFIED)

    同时的话 万一在布局文件RatioLayout中的itcast:ratio=“0” 等于0是什么后果呢 将来它是宽/高的比 写一个0直接就挂掉了 也不能小于0 所以直接写成

    && ratio>0

    根据计算后的控件高度以及模式,重新定义高度值 即heightMeasureSpec

    这样当调super.onMeasure(widthMeasureSpec, heightMeasureSpec);在底层去设置它的宽高的时候,就会用新的宽高去做


    list_item_subject.xml

    <com.itcast.googleplay.ui.widget.RatioLayout

      android:layout_width="match_parent"
    
      android:layout_height="wrap_content"
    
      itcast:ratio="2.43"
    

    <ImageView

      android:id="@+id/iv_pic"
    
      android:layout_width="match_parent"
    
      android:layout_height="match_parent"
    
      android:padding="5dp"
    
      android:scaleType="fitXY"
    
       />
    

    </com.bq.googleplay.ui.widget.RatioLayout>

    2.我们需要告诉它宽高比例是多少 这时候就可以通过属性的方式

    去把这个比例传给RatioLayout 那我们这时候就要自定义一个属性

    自定义属性 在手机卫士中应该讲过 智慧北京中好像也用到过

    那自定义属性的话 其实是在我们的values里面去创建一个attrs.xml

    而这个xml的具体写法它比较多 如果实在记不住的话

    你可以从android源码中去抄

    找到android sdk 下边有一个plaforms 找android18

    data/res/values/attrs.xml 它里面是叫做<declare-styeable

    它里边是一个一个的attr的item条目 它的类型format应该是一个宽高的比例

    比例是一个小数 即是float类型的 在源码的attrs.xml中查找float

    最后查到

    6.用RatioLayout把imageView塞进去 这时候我们就去计算RatioLayout的宽高就可以了 让这个ImageView完完全全的去填充这个RatioLayout控件就可以了

    所以ImageView的height写一个matchParent去填充父控件就可以了

    而它的scaleType压缩也就没有意义去写了,删除掉即可 其实不删也可以 设置成fitXY 就是适应图片的宽高 而RatioLayout中早都将宽高完全确定了

    那父控件RatioLayout宽度match_parent填充父控件 高度我们也没有确定而是要根据它的比例ratio=2.43去计算它的高度的

    这时候要动态的去修改这个RatioLayout的宽高的话 我们就重写了一下它的onMeasure方法 在onMeasure方法里边呢 我们再去重新修改布局的尺寸

    而它会回传回来目前的这样一个尺寸 widthMeasureSpec和heightMeasureSpec 当然我们要拿到具体的宽高度值 即widthSize 和heightSize 还有它的宽高度的类型 widthMode heightMode

    类型对应三种AT_MOST,EXACTLY,UNSPECIFIED


自定义属性

	values/attrs.xml



	<?xml version="1.0" encoding="utf-8"?>

	<resources>

	

	    <declare-styleable name="RatioLayout">

	        <attr name="ratio" format="float" />

	    </declare-styleable>

	

	</resources>



declare-styleable的name 就叫做RatioLayout  

具体属性attr的name就叫做ratio 比例format是float类型的



3.自定义属性完了之后就可以回到布局文件list_item_subject.xml

中去使用这个自定义属性了

要使用自定义属性那就需要一个域名空间 

在list_item_subject.xml中将

xmlns:android="http://schemas.android.com/apk/res/android"

抄一份 然后将前边这个android改为itcast名字(这个名字可以自己起,下边自定义属性 要用到) 

将后边这个android改为包名com.itcast.googleplay 

(即打开清单文件可查看包名)

这时候就可以给<com.itcast.googleplay.ui.widget.RatioLayout

搞一个自定义属性 itcast:ratio="2.43"

-----------------------------------------------------------

Day03 12.星际效果自定义控件使用&推荐模块开发 ##

推荐页面实现 ##

推荐的网络数据也是写死在了我们的服务器代码中 

很简单 就是数组中 是一个一个的字符串 所以范型字符串的集合即ArrayList<String>

网络数据加载

	/**
  • 推荐页访问网络
     *

  • @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;
    
      	}
    
      }
    

飞入飞出自定义控件

拷贝飞入飞出自定义控件的相关代码

新建com.bq.googleplay.ui.widget.fly包

拷贝

AnimationUtils.java 

RandomLayout.java

ShakeListener.java

StellarMap.java



AnimationUtils是动画的类 推荐中的效果是滑动屏幕往上的话 字体是越来越小 往下拉的话是 字体越来越大

RandomLayout按一定的情况 把这个屏幕划分为一个又一个的随机的格子

来填充我们想填充的东西  我们现在填充的是TextView 填充ImageView也可以

ShakeListener暂时没有用到 它是监听摇一摇动作的那个类  即有时候用户拿手机刷刷刷一摇 刷的就刷新了 其实要用的话也很简单 它的底层用到了android系统的SenorManager 传感器的这样一个东西 它可以利用传感器计算出手机这样当时的一个位置 X方向 Y方向这样的一个动作 其实现在很多的产品都用到了传感器 最典型的就是微信的摇一摇 手机里面其实是有一个硬件 就是在监听这个摇一摇的动作  

甚至手机上一些健身类的软件 你把手机装在身上,去跑步的话,它就能给你计步  记住你今天跑了多少步 

StellarMap是我们的核心类 就是推荐模块实现的星际效果 

------------------------------------------------------

Day03 13.文字随机大小随机颜色 ##

RecommendFragment.java



/**
  • 推荐
     *

  • @author Kevin
     * 
     */
     public class RecommendFragment extends BaseFragment {

      private ArrayList<String> data;
    
    
    
      @Override
    
      public View onCreateSuccessView() {
    
      	// 初始化 飞入飞出自定义控件
    
      	StellarMap stellar = new StellarMap(UIUtils.getContext());
    
      	//1.设置内部文字距边缘边距为10dip
    
      	int padding = UIUtils.dip2px(10);
    
      	stellar.setInnerPadding(padding, padding, padding, padding);
    
      	//2.设置数据源
    
      	stellar.setAdapter(new RecommendAdapter());
    
      	//3.设定展示规则,9行6列(具体以随机结果为准)
    
      	stellar.setRegularity(6, 9);
    
      	//4.设置默认组为第0组
    
      	stellar.setGroup(0, true);
    
    
    
      	return stellar;
    
      }
    
    
    
      @Override
    
      public ResultState onNetLoad() {
    
      	RecommendProtocol protocol = new RecommendProtocol();
    
      	data = protocol.getData(0);// 33条数据
    
      	return check(data);
    
      }
    
      
    
      class RecommendAdapter implements StellarMap.Adapter {
    
      	// 返回组的数量 划一下显示一组
    
      	@Override
    
      	public int getGroupCount() {
    
      		return 2;
    
      	}
    
    
    
      	// 返回某个组的孩子数量
    
      	@Override
    
      	public int getCount(int group) {
    
      		//每组个数=总数/组数
    
      		int count = data.size() / getGroupCount();
    
      		//要考虑除不尽的情况,要将余数追加到最后一组
    
      		if (group == getGroupCount() - 1) {//最后一组
    
      			count += data.size() % getGroupCount();
    
      		}
    
    
    
      		return count;
    
      	}
    
    
    
      	@Override
    
      	public View getView(int group, int position, View convertView) {
    
      		//group 第几组 
    
      		//position 第几个孩子 新的一组数据,position都会从0开始
    
      		//convertView 
    
      		if (group > 0) {// 如果发现不是第一组,需要更新position, 要加上之前几页的总数,才是当前组的准确位置
    
      			position = position + getCount(group - 1) * group;
    
      		}
    
    
    
      		TextView view = new TextView(UIUtils.getContext());
    
      		view.setText(data.get(position));
    
    
    
      		//5.设置随机文字大小
    
      		Random random = new Random();
    
      		// 产生16-25的随机数字体大小
    
      		int size = 16 + random.nextInt(10);
    
      		// 以sp为单位设置文字大小 用sp屏幕适配比较好
    
      		view.setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
    
    
    
      		//6.设置随机文字颜色
    
      		// 产生30-239的随机颜色, 绕过0-29
    
      		int red = 30 + random.nextInt(210);
    
      											// 240-255的值,避免颜色过暗或者过亮
    
      		int green = 30 + random.nextInt(210);
    
      		int blue = 30 + random.nextInt(210);
    
      		view.setTextColor(Color.rgb(red, green, blue));
    
    
    
      		return view;
    
      	}
    
    
    
      	@Override
    
      	public int getNextGroupOnZoom(int group, boolean isZoomIn) {
    
      		System.out.println("isZoomIn:" + iszoomIn);
    
      	
    
      		if (!isZoomIn) {
    
      			// 下一组
    
      			if (group < getGroupCount() - 1) {
    
      				return ++group;
    
      			} else {
    
      				return 0;// 如果已经是最后一组,就跳到第一组
    
      			}
    
      		} else {
    
      			// 上一组
    
      			if (group > 0) {
    
      				return --group;
    
      			} else {
    
      				return getGroupCount() - 1;// 如果已经是第一组了,就跳到最后一组
    
      			}
    
      		}
    
      	}
    
      }
    

    }


Day03 14.排行模块开发 ##

排行模块实现 ##

网络数据加载

和推荐模块类似 数据也是写死在服务器代码中 很简单 就是数组中 是一个一个的字符串 所以范型字符串的集合即ArrayList HotProtocol.java

/**
  • 排行页访问网络,加载数据
     *

  • @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;
    
      }
    

    }


    将自定义控件FlowLayout.java拷贝到widget包下

HotFragment.java

	/**
  • 排行
     *

  • @author Kevin
     * 
     */
     public class HotFragment extends BaseFragment {

      	private ArrayList<String> data;
    
    
    
      @Override
    
      public View onCreateSuccessView() {
    
      	
    
      	// 为了使布局可以上下滑动,需要用ScrollView包装起来
    
      	ScrollView scrollView = new ScrollView(UIUtils.getContext());
    
      	int padding = UIUtils.dip2px(10);
    
      	// 设置ScrollView内边距10dp
    
      	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 : data) {
    
      		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 red = 30 + random.nextInt(210);
    
      		int green = 30 + random.nextInt(210);
    
      		int blue = 30 + random.nextInt(210);
    
    
    
      		// 默认背景
    
      		GradientDrawable normal = DrawableUtils.getGradientDrawable(
    
      				Color.rgb(red, green, blue), UIUtils.dip2px(6));
    
      		//按下背景
    
      		int color = 0xffcecece;// 按下后偏白的背景色
    
      		GradientDrawable press = DrawableUtils.getGradientDrawable(
    
      				color, UIUtils.dip2px(6));
    
      		//生成状态选择器
    
      		StateListDrawable selector = DrawableUtils.getSelector(normal,press);
    
      		// 设置圆角矩形背景
    
      		view.setBackgroundDrawable(selector);
    
    
    
      		// TextView本身不具备被点击的特点 所以必须设置点击事件, 按下后颜色才会变化
    
      		view.setOnClickListener(new OnClickListener() {
    
    
    
      			@Override
    
      			public void onClick(View v) {
    
      				Toast.makeText(UIUtils.getContext(), str,
    
      						Toast.LENGTH_SHORT).show();
    
      			}
    
      		});
    
    
    
      		// 将TextView添加给自定义控件
    
      		flow.addView(view);
    
      	}
    
    
    
      	scrollView.addView(flow);
    
    
    
      	return scrollView;
    
      }
    
    
    
      @Override
    
      public ResultState onNetLoad() {
    
      	HotProtocol protocol = new HotProtocol();
    
      	data = protocol.getData(0);
    
      	return check(data);
    
      }
    

    }


创建圆角矩形对象

排行中要有一个圆角矩形的背景 圆角矩形在讲手机卫士的时候已经讲过  

但是那会儿是用一个xml的shape中设置圆角矩形的角的弧度  包括它的颜色之类的

现在不能这样做了 颜色是随机的 你不可能在一个xml中写一个随机颜色 在xml中要写就要写确定,不是一个随机的  那这时候我们只能通过代码的方式 去生成一个shape的这样一个形状 而且也是一个圆角矩形 

这时候要去做的话,写一个工具类DrawableUtils 专门去生成圆角矩形 



DrawableUtils.java



	/**
  • 生成图像的工具类
     *

  • @author Kevin
     */
     public class DrawableUtils {

      	/**
    
  • 生成一个特定颜色的圆角矩形
     *

  • @param rgb

  •        颜色值
    
  • @param radius

  •        圆角半径
    
  • @return
     */
     public static Drawable getGradientDrawable(int rgb, int radius) {
     // 初始化Shape形状的对象
     GradientDrawable drawable = new GradientDrawable();
     // 矩形类型
     drawable.setGradientType(GradientDrawable.RECTANGLE);
     // 设置颜色
     drawable.setColor(rgb);
     // 设置圆角半径
     drawable.setCornerRadius(radius);
     return drawable;
     }

      	/**
    
  • 获取状态选择器对象(selector)
     *

  • @param normal

  •        默认图像
    
  • @param pressed

  •        按下图像
    

*/
 public static StateListDrawable getSelector(Drawable normal,
 Drawable press) {
 StateListDrawable selector = new StateListDrawable();
 //设置按下状态的图片
 selector.addState(new int[] { android.R.attr.state_pressed }, press);
 //设置默认状态下的图片
 selector.addState(new int[] {}, normal);
 return selector;
 }
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值