一.框架的使用
①.support-v4包下的ViewPager
vpGuide = (ViewPager) findViewById(R.id.vp_guide);
vpGuide.setAdapter(new MyPagerAdapter());// 设置适配器
vpGuide.setCurrentItem(Position)//设置Viewpager的位置
vpGuide.setOnPageChangeListener(new GuidePageListener());// 设置滑动监听
// pagerAdapter适配器
class MyPagerAdapter extends PagerAdapter {
@Override
public int getCount() {
return mImageIds.length;
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(imageViews.get(position));//添加VIew到Viewpager中
return imageViews.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);//移除View
}
}
②.slideMenu框架,实现侧边栏(onViewdrap 也可以实现。qq侧边栏)
---导入SlideMenuLibrary包
---使用SlideMenu
<span style="white-space:pre"> </span>setBehindContentView(R.layout.left_menu);// 设置侧边栏
SlidingMenu slidingMenu = getSlidingMenu();// 获取侧边栏对象
slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);// 设置全屏触摸
int width = getWindow().getWindowManager().getDefaultDisplay().getWidth();
slidingMenu.setBehindOffset(width*2/3);// 设置contentFragment预留屏幕的宽度也就是侧边栏为3分之一屏幕宽度
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>/**是否隐藏左侧菜单页*/
<span style="white-space:pre"> </span>protected void toggleSlideMenu() {
<span style="white-space:pre"> </span>SlidingMenu slidingMenu = mainUi.getSlidingMenu();
<span style="white-space:pre"> </span>slidingMenu.toggle();//显示时隐藏 .隐藏时显示
<span style="white-space:pre"> </span>}
---填充Fragment到侧边栏中
<span style="white-space:pre"> </span>// 填充内容
beginTransaction.replace(R.id.fl_left_menu, new LeftMenuFragment(),FRAGMENT_LEFT_MENU);
③.ViewPagerIndicatorLibrary框架(为Viewpager设置一个Tab栏)
---设置xml控件(放置在ViewPager的上方)
<span style="white-space:pre"> </span><com.viewpagerindicator.TabPageIndicator
android:id="@+id/titles"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
---代码设置
<span style="white-space:pre"> </span>mIndicator = (TabPageIndicator) view.findViewById(R.id.titles);
<span style="white-space:pre"> </span>mviewPager.setAdapter(detailadapter);
//必须在VIewPager设置adapter之后才能调用ViewpagerIndicator
mIndicator.setViewPager(mviewPager);
---设置tab数据
<span style="white-space:pre"> </span>class MenuDetailadapter extends PagerAdapter{
<span style="white-space:pre"> </span>.......
public CharSequence getPageTitle(int position) {
// 设置标题在PagerAdapter中
return newsTabData.get(position).title;
}
<span style="white-space:pre"> </span>}
----设置主题样式
<span style="white-space:pre"> </span><activity
android:name="com.example.newbj.MainActivity"
android:theme="@style/Theme.PageIndicatorDefaults" >
</activity>
-----修改主题样式
<span style="white-space:pre"> </span> <item name="android:background">@drawable/vpi__tab_indicator</item><!-- 背景设置 -->
<span style="white-space:pre"> </span> <item name="android:textColor">@drawable/vpi__tab_text_color_indicator</item><!-- 文字背景-->
④.ViewPagerIndicatorLibrary框架实现位置指示器
----xml布局及样式(使用相对布局和ViewPager叠加)
<span style="white-space:pre"> xmlns:app="http://schemas.android.com/apk/res-auto"</span>
<span style="white-space:pre"> </span><com.viewpagerindicator.CirclePageIndicator
<span style="white-space:pre"> </span> android:id="@+id/indicator"
<span style="white-space:pre"> </span>android:layout_width="wrap_content"
<span style="white-space:pre"> </span>android:layout_height="wrap_content"
<span style="white-space:pre"> </span> android:layout_alignParentRight="true"
<span style="white-space:pre"> </span>android:padding="10dip"
<span style="white-space:pre"> </span> app:fillColor="#f00"
<span style="white-space:pre"> </span> app:pageColor="@android:color/darker_gray"
<span style="white-space:pre"> </span> app:radius="3dp"
<span style="white-space:pre"> </span> app:strokeWidth="0dp" />
---代码设置
<span style="white-space:pre"> </span>mViewPager.setAdapter(new TopNewsAdapter());
// 因为这个和viewpager一起在用,所以让这个设置监听,不然不会改变指示器的移动
indicator.setOnPageChangeListener(this);
indicator.setViewPager(mViewPager);
indicator.setSnap(true);// 支持快照显示
indicator.onPageSelected(0);// 默认设置为第一个点
二.拦截事件( dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent)
1.大的VIewpager设置不拦截.同时设置不处理touch事件.里面的VIewpager就可以滑动
<span style="white-space:pre"> </span>public boolean onInterceptTouchEvent(MotionEvent arg0) {
//表示不拦截事件
return false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// 表示viewPager什么都不做
return false;
}
2.当TabPageIndicator 向左时会被SlideMenu拦截事件 (如果设置SlideMenu的设置不拦截和不处理touch事件的话很复杂)
解决方法:①TabPageIndicator 请求不要拦截 在TabPageIndicator中请求
当ViewPager向左滑动时也会被SlideMenu拦截事件
解决方法:②ViewPager 请求不要拦截 自定义Viewpager
<span style="white-space:pre"> </span>/**
* 事件分发, 请求父控件及祖宗控件不要拦截事件
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
getParent().requestDisallowInterceptTouchEvent(true);// 用getParent去请求
return super.dispatchTouchEvent(ev);
}
3.设置ViewPager北京第一页和 最后一页需要被拦截
<span style="white-space:pre"> </span>/*
* 请求上面的viewpager不要拦截这个滑动事件
*
* 1.如果是右滑到最后一张图片需要父控件拦截
* 2.如果是左滑到第一种图片需要父控件拦截
* 3.如果是上下滑动需要父控件拦截
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = ev.getX();
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
endX = ev.getX();
endY = ev.getY();
if (Math.abs(endX - startX) - Math.abs(endY - startY) > 0) {// 左右滑动
if (endX - startX > 0) {// 向右滑动
if (getCurrentItem() == 0) {// 是第一张图片
getParent().requestDisallowInterceptTouchEvent(false);// 拦截
}else{
getParent().requestDisallowInterceptTouchEvent(true);// 不拦截
}
} else {// 向左滑动
if (getCurrentItem() == (getAdapter().getCount() - 1)) {// 最后一张图片
getParent().requestDisallowInterceptTouchEvent(false);// 拦截
}else{
getParent().requestDisallowInterceptTouchEvent(true);// 不拦截
}
}
} else {// 上下滑动拦截
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
三.解析json数据(通过Gson解析)
通过HttpUtils获取数据:
<span style="white-space:pre"> </span>HttpUtils httpUtils = new HttpUtils();
httpUtils.send(HttpMethod.GET, moreUrl, new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = (String) responseInfo.result;
System.out.println("返回数据" + result);
parseData(result, true);
listView.OnRefreshComplete(true);
}
@Override
public void onFailure(HttpException error, String msg) {
Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT).show();
error.printStackTrace();
listView.OnRefreshComplete(true);
}
});
1.解析数据
<span style="white-space:pre"> </span>Gson gson = new Gson();
mNewsData = gson.fromJson(result, NewsData.class);
System.out.println("解析数据" + mNewsData);
2.NewsData类对json实现定义
/*
* 网络分类信息的封装
*/
public class NewsData {
//定义的名字必须和服务器返回的名字一致
public int retcode;
public ArrayList<NewsMenuData> data;
//public ArrayList extend;不需要这个
//侧边栏的数据对象
public class NewsMenuData{
public String id;
public String title;
public int type;
public String url;
public ArrayList<NewsTabData> children;
@Override
public String toString() {
return "NewsMenuData [title=" + title + ", children=" + children
+ "]";
}
}
//新闻页签下的11个标签对象
public class NewsTabData{
public String id;
public String title;
public int type;
public String url;
@Override
public String toString() {
return "NewsTabData [title=" + title + "]";
}
}
@Override
public String toString() {
return "NewsData [data=" + data + "]";
}
}
四.ListView下拉刷新(详情见前面笔记)
1.初始化数据
---3种状态
--下拉刷新状态
--释放刷新状态
--正在刷新状态
this.addHeaderView(headerView);
headerView.measure(0, 0);// 通知测量
measuredHeight = headerView.getMeasuredHeight();
// 默认将其隐藏
headerView.setPadding(0, -measuredHeight, 0, 0);
2.设置下拉逻辑(通过判断触摸的事件)
//处理下拉刷新的滑动事件
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1) {
startY = ev.getRawY();
}
if (State == STATE_REFRESHING) {
break;// 如果正在刷新,设置不能下拉。返回
}
endY = ev.getRawY();
float dy = endY - startY;
// 如果是下拉并且是第一个position才下拉
if (dy > 0 && getFirstVisiblePosition() == 0) {
int padding = (int) (dy - measuredHeight);
headerView.setPadding(0, padding, 0, 0);
// 状态改为释放刷新
if (padding > 0 && State != STATE_RELEASE_REFRESH) {
State = STATE_RELEASE_REFRESH;
StateChange();
// 状态改为下拉刷新
} else if (padding < 0 && State != STATE_PULL_REFRESH) {
State = STATE_PULL_REFRESH;
StateChange();
}
return true;// 避免其他事件处理
}
break;
case MotionEvent.ACTION_UP:
startY = -1;
if (State == STATE_RELEASE_REFRESH) {// 如果是释放刷新 变为正在刷新
State = STATE_REFRESHING;
headerView.setPadding(0, 0, 0, 0);
StateChange();
} else if (State == STATE_PULL_REFRESH) {// 如果是松开
// State = STATE_RELEASE_REFRESH;
// 隐藏标题
headerView.setPadding(0, -measuredHeight, 0, 0);
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
//通过状态改变布局显示
private void StateChange() {
switch (State) {
case STATE_PULL_REFRESH:// 下拉状态
tv_title.setText("下拉刷新");
iv_arr.setVisibility(View.VISIBLE);
pb_progress.setVisibility(View.INVISIBLE);
iv_arr.startAnimation(downanim);
break;
case STATE_RELEASE_REFRESH:// 松开状态
tv_title.setText("松开刷新");
iv_arr.setVisibility(View.VISIBLE);
pb_progress.setVisibility(View.INVISIBLE);
iv_arr.startAnimation(refreshanim);
break;
case STATE_REFRESHING:// 正在刷新状态
tv_title.setText("正在刷新...");
iv_arr.clearAnimation();// 必须先清除动画才可以设置不可见
iv_arr.setVisibility(View.INVISIBLE);
pb_progress.setVisibility(View.VISIBLE);
//当加载完数据回调监听
if (onCompleteRefreshListener != null) {
onCompleteRefreshListener.noShowRefresh();
}
break;
}
}
3.当加载完数据时调用完成刷新逻辑.隐藏下拉刷新
// 完成刷新之后就将其隐藏的方法
public void OnRefreshComplete(boolean isLoadMore) {
if (isLoadMore) {
// 隐藏脚
footerView.setPadding(0, -footViewHeight, 0, 0);
} else {
State = STATE_PULL_REFRESH;
tv_title.setText("下拉刷新");
iv_arr.setVisibility(View.VISIBLE);
pb_progress.setVisibility(View.INVISIBLE);
// 刷新完之后就隐藏头
headerView.setPadding(0, -measuredHeight, 0, 0);
tv_time.setText("最后刷新:" + getCurrentTime());
}
}
五.ListView加载更多
1.初始化数据
<span style="white-space:pre"> </span>this.addFooterView(footerView);
// 自定义的view获得高需要去先通知测量
footerView.measure(0, 0);
footViewHeight = footerView.getMeasuredHeight();
footerView.setPadding(0, -footViewHeight, 0, 0);
2.通过监听滑动的状态设置加载更多
this.setOnScrollListener(this);
/** 加载更多监听 */
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_FLING
|| scrollState == SCROLL_STATE_IDLE) {// 如果是空闲或者是滑动
if (getLastVisiblePosition() == getCount() - 1) {// 如果是最后一个
// 显示加载更多
// footerView.setVisibility(View.VISIBLE);
footerView.setPadding(0, 0, 0, 0);
setSelection(getCount() - 1);
// 设置监听,当加载完数据之后就回调隐藏加载更多
if (onCompleteRefreshListener != null) {
onCompleteRefreshListener.noShowLoadMore();
}
}
}
}
3.完成加载更多之后隐藏加载更多
<span style="white-space:pre"> </span>// 完成刷新之后就将其隐藏的方法
public void OnRefreshComplete(boolean isLoadMore) {
if (isLoadMore) {
// 隐藏脚
footerView.setPadding(0, -footViewHeight, 0, 0);
}
六.3级缓存
1.设置缓存类
public class MybitmpaUtils {
private NetworkCacheUtils networkCacheUtils;
private SdcardCacheUtils sdcardCacheUtils;
private RoomCacheUtils roomCacheUtils;
public MybitmpaUtils() {
roomCacheUtils = new RoomCacheUtils();
sdcardCacheUtils = new SdcardCacheUtils();
networkCacheUtils = new NetworkCacheUtils(sdcardCacheUtils,roomCacheUtils);
}
public void display(ImageView ivPic,String url){
ivPic.setImageResource(R.drawable.news_pic_default);//设置默认图片
//1.从内存中读取,最快
Bitmap bitmapRoom = roomCacheUtils.getDataByRoom(url);
if(bitmapRoom!=null){
ivPic.setImageBitmap(bitmapRoom);
System.out.println("从内存中读取到了");
return;
}
//2.从sdcard中读取。第二
Bitmap bitmap = sdcardCacheUtils.getDatabySdcard(url);
if(bitmap!=null){
ivPic.setImageBitmap(bitmap);
roomCacheUtils.setDatatoRoom(url, bitmap);//保存一份到内存中
System.out.println("从sdcard中读取到了");
return;
}
//3.从网络中读取,第三
networkCacheUtils.getBitmapFromNet(ivPic, url);
}
}
2.网络加载使用AsyncTask
//网络读取数据
public class NetworkCacheUtils {
private HttpURLConnection coon;
private SdcardCacheUtils sdcardCacheUtils;
private RoomCacheUtils roomCacheUtils;
public NetworkCacheUtils(SdcardCacheUtils sdcardCacheUtils, RoomCacheUtils roomCacheUtils) {
this.sdcardCacheUtils = sdcardCacheUtils;
this.roomCacheUtils = roomCacheUtils;
}
// 根据传入的url给传入的ImageView设置图片
public void getBitmapFromNet(ImageView ivPic, String url) {
new BitmapTask().execute(ivPic, url);// 执行异步下载
}
public class BitmapTask extends AsyncTask<Object, Void, Bitmap> {
private ImageView imageView;
private String url;
// 执行前调用。初始化数据
@Override
protected void onPreExecute() {
super.onPreExecute();
}
//异步下载数据
@Override
protected Bitmap doInBackground(Object... params) {
imageView = (ImageView) params[0];
url = (String) params[1];
imageView.setTag(url);
return downloadBitmap(url);
}
//
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
//当doInbackground下载完之后会将结果返回到这里(主线程运行)
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (result != null) {
//获取绑定的Bitmap对象的url
String bindurl = (String) imageView.getTag();
// 防止图片的重用导致的错乱
if (url.equals(bindurl)) {
sdcardCacheUtils.saveDatatoSdcard(result, bindurl);
roomCacheUtils.setDatatoRoom(bindurl, result);
imageView.setImageBitmap(result);//设置图片
System.out.println("从网络拿到图片了。。。。");
}
}
}
}
/*
* 下载图片的逻辑
*/
private Bitmap downloadBitmap(String url) {
try {
coon = (HttpURLConnection) new URL(url).openConnection();
coon.setConnectTimeout(5000);
coon.setReadTimeout(5000);
coon.setRequestMethod("GET");
coon.connect();
int responseCode = coon.getResponseCode();
if (responseCode == 200) {
InputStream inputStream = coon.getInputStream();
//对图片进行压缩
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2 ;//宽高缩小为原来的2分之一
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(inputStream,null,options);
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
coon.disconnect();
}
return null;
}
}
3.Sdcard缓存
public class SdcardCacheUtils {
private static final String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/nzhbj";
public void saveDatatoSdcard(Bitmap bitmap, String url) {
String fileName;
try {
fileName = MD5Encoder.encode(url);//根据图片的url设置名字
File file = new File(path, fileName);//创建File对象
//如果没有父文件夹。则创建
File parentFile = file.getParentFile();
if (!parentFile.exists()) {
parentFile.mkdirs();
}
FileOutputStream fileOutputStream = new FileOutputStream(file);
//保存图片到流中 jpeg格式 100是不压缩
bitmap.compress(CompressFormat.JPEG, 100, fileOutputStream);
} catch (Exception e1) {
e1.printStackTrace();
}
}
//从Sdcard中获取bitmap对象
public Bitmap getDatabySdcard(String url){
try {
String fileName = MD5Encoder.encode(url);
File file = new File(path,fileName);
if(file.exists()){
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
4.内存缓存
public class RoomCacheUtils {
//一般对象:HashMap<String, Bitmap> hashMap = new HashMap<String, Bitmap>();
//软引用对象:HashMap<String, SoftReference<Bitmap>>hashMap = new HashMap<String, SoftReference<Bitmap>>();
//SoftReference<Bitmap> reference = hashMap.get(url);
private LruCache<String, Bitmap>lruCache; //使用最少最近算法
public RoomCacheUtils(){
//设置lruCache缓存的大小一半为总内存的8分之一
float maxSize = Runtime.getRuntime().maxMemory()/8;//模拟器默认是16m
lruCache = new LruCache<String, Bitmap>((int)maxSize){
@Override
protected int sizeOf(String key, Bitmap value) {
//返回一张图片的大小
int rowBytes = value.getByteCount();
return rowBytes;
}
};
}
//保存对象在内存中
public void setDatatoRoom(String url , Bitmap bitmap ){
lruCache.put(url, bitmap);
}
//从内存中读取Bitmap对象
public Bitmap getDataByRoom(String url){
Bitmap bitmap = lruCache.get(url);
return bitmap;
}
}
5.内存缓存避免oom
----使用LRU算法加载bitmap对象
----压缩图片
<span style="white-space:pre"> </span>//对图片进行压缩
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2 ;//宽高缩小为原来的2分之一
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(inputStream,null,options);
return bitmap;
七.屏幕适配(见第6天)