项目开发框架搭建二
来源:互联网 发布:g11s变频器的数据复写 编辑:程序博客网 时间:2024/05/18 15:08
主MainActivity-activity.main.xml
<?xml version="1.0" encoding="utf-8"?><com.zhy.autolayout.AutoLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <history.six.com.rushingdemo.view.LazyViewPager android:layout_width="match_parent" android:layout_height="0px" android:layout_weight="1" android:id="@+id/main_lazyViewPager"> </history.six.com.rushingdemo.view.LazyViewPager> <RadioGroup android:id="@+id/rg" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:background="@mipmap/bottom" android:gravity="center_vertical" android:paddingTop="10px" android:paddingBottom="10px" android:orientation="horizontal"> <RadioButton android:id="@+id/rb_home" android:layout_width="0dp" android:checked="true" android:layout_height="wrap_content" android:layout_weight="1" android:background="#f5f5f5" android:button="@null" android:drawableTop="@drawable/duobao_selector" android:gravity="center" android:textColor="@drawable/bottom_text_color" android:text="夺宝" /> <RadioButton android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#f5f5f5" android:button="@null" android:drawableTop="@drawable/category_selector" android:gravity="center" android:text="分类" android:textColor="@drawable/bottom_text_color"/> <RadioButton android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#f5f5f5" android:button="@null" android:drawableTop="@drawable/newjiexiao_selector" android:gravity="center" android:text="最新揭晓" android:textColor="@drawable/bottom_text_color"/> <RadioButton android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#f5f5f5" android:button="@null" android:drawableTop="@drawable/order_selector" android:gravity="center" android:text="订单" android:textColor="@drawable/bottom_text_color"/> <RadioButton android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="#f5f5f5" android:button="@null" android:drawableTop="@drawable/mine_selector" android:gravity="center" android:text="我的" android:textColor="@drawable/bottom_text_color" android:id="@+id/radioButton" /> </RadioGroup></com.zhy.autolayout.AutoLinearLayout>
Build.gradle依赖
//屏幕适配依赖 compile 'com.zhy:autolayout:1.4.5' //android5.0控件的使用依赖 compile 'com.android.support:design:25.1.1' //retrofit网络访问 compile 'com.squareup.retrofit2:retrofit:2.0.0-beta2' compile 'com.squareup.retrofit2:converter-scalars:2.0.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' //fresco图片加载 compile 'com.github.CarGuo:FrescoUtils:v1.0.4' compile project(':library') //轮播图 compile 'com.youth.banner:banner:1.4.8' //Glide图片加载 compile 'com.github.bumptech.glide:glide:3.7.0'
整体布局格式
MyApplication
package history.six.com.rushingdemo.application;import android.app.Application;import android.content.Context;import android.os.Handler;import android.os.Process;import com.facebook.drawee.backends.pipeline.Fresco;import com.facebook.imagepipeline.core.ImagePipelineConfig;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class MyApplication extends Application { private static Context context; private static Handler handler; private static int mainThreadId; private static Thread currentThread; private static ExecutorService executorService; @Override public void onCreate() { super.onCreate(); /** * 初始化Fresco */ ImagePipelineConfig config = ImagePipelineConfig.newBuilder(this) .setDownsampleEnabled(true) .build(); Fresco.initialize(this, config); context = getApplicationContext(); //获取handler handler = new Handler(); //获取主线程id mainThreadId = Process.myTid(); //获取当前线程 currentThread = Thread.currentThread(); //创建线程池 executorService = Executors.newFixedThreadPool(5); } /** * 获取整个应用的上下文 */ public static Context getContext(){ return context; } public static ExecutorService getThreadPoll() { return executorService; } public static Thread getMainThread() { return currentThread; } public static int getMainThreadId() { return mainThreadId; } public static Handler getHandler() { return handler; }}
**
BaseData
//1.网络缓存数据存放的路径
//2.NOTIME不设置缓存的保存多久,NORMALTIME给定一个缓存的保存时间限制//3.第一次获取数据从网络中获取,之后缓存数据到我们的本地中之后直接从本地拿到数据即可//4.缓存的过程中涉及到读取和写入的操作
**
package history.six.com.rushingdemo.base;import android.text.TextUtils;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.FileReader;import java.io.FileWriter;import java.util.HashMap;import java.util.Set;import history.six.com.rushingdemo.application.MyApplication;import history.six.com.rushingdemo.manager.HttpMangerUtils;import history.six.com.rushingdemo.utils.CommonUtils;import history.six.com.rushingdemo.utils.Md5;import history.six.com.rushingdemo.utils.ToastUtil;import history.six.com.rushingdemo.view.ShowingPager;import retrofit2.Call;import retrofit2.Callback;import retrofit2.Response;public abstract class BaseData { private final File fileDir; public static final int NOTIME = 0; public static final int NORMALTIME = 3 * 24 * 60 * 60 * 1000; //1.网络缓存数据存放的路径 //2.NOTIME不设置缓存的保存多久,NORMALTIME给定一个缓存的保存时间限制 //3.第一次获取数据从网络中获取,之后缓存数据到我们的本地中之后直接从本地拿到数据即可 //4.缓存的过程中涉及到读取和写入的操作 //缓存数据存到哪里? public BaseData() { //找到存储路径 File cacheDir = MyApplication.getContext().getCacheDir(); fileDir = new File(cacheDir, "meiru"); if (!fileDir.exists()) { //创建文件夹 fileDir.mkdir(); } } /** * @param baseUrl //基础路径 * @param path //请求的实体内容 * @param validTime //有效时间 */ public void getData(String baseUrl, String path, int validTime) { //先判断有效时间 if (validTime == 0) { //直接请求网络,要最新数据 getDataFromNet(baseUrl, path, validTime); } else { //从本地获取 String data = getDataFromLocal(path, validTime); if (TextUtils.isEmpty(data)) { //如果为空,请求网络 getDataFromNet(baseUrl, path, validTime); } else { //拿到了数据,返回数据 setResultData(data); } } } /** * 从网络获取数据 * * @param path * @param validTime */ private void getDataFromNet(String baseUrl, final String path, final int validTime) { HttpMangerUtils.getData(baseUrl, path, new Callback<String>() { @Override public void onResponse(Call<String> call, final Response<String> response) { CommonUtils.runOnUIThread(new Runnable() { @Override public void run() { //将数据抽象出去 setResultData(response.body()); } }); //将数据写入本地缓存 writeDataToLocal(path, validTime, response.body()); } @Override public void onFailure(Call<String> call, Throwable t) { ToastUtil.show(MyApplication.getContext(), "请求出错,代码" + t.getMessage()); setResulttError(ShowingPager.STATE_LOAD_ERROR); } }); } /** * @param isReadCookie 判断是否读取Cookie * @param isSaveCookie 判断是否保存Cookie * @param baseUrl 设置基础url * @param path 标准url * @param argsMap 请求参数实体内容 * @param validTime 设置超时时间 */ public void postData(boolean isReadCookie, boolean isSaveCookie, String baseUrl, String path, HashMap<String, String> argsMap, int validTime) { Set<String> strings = argsMap.keySet(); StringBuilder stringBuilder = new StringBuilder(); for (String key : strings) { stringBuilder.append(key).append(argsMap.get(key)); } //先判断有效时间 if (validTime == 0) { //直接请求网络,要最新数据 postDataFromNet(isReadCookie, isSaveCookie, baseUrl, path, stringBuilder.toString(), argsMap, validTime); } else { //从本地获取 String data = getDataFromLocal(baseUrl + path + stringBuilder.toString(), validTime); if (TextUtils.isEmpty(data)) { //如果为空,请求网络 postDataFromNet(isReadCookie, isSaveCookie, baseUrl, path, stringBuilder.toString(), argsMap, validTime); } else { //拿到了数据,返回数据 setResultData(data); } } } /** * @param isReadCookie 判断是否读取Cookie * @param isSaveCookie 判断是否保存Cookie * @param baseUrl 设置基础url * @param path 标准url * @param keyValues 请求数据的参数key+values * @param argsMap 请求参数实体内容 * @param validTime 设置超时时间 */ private void postDataFromNet(boolean isReadCookie, boolean isSaveCookie, final String baseUrl, final String path, final String keyValues, final HashMap<String, String> argsMap, final int validTime) { HttpMangerUtils.postMethod(isReadCookie, isSaveCookie, baseUrl, path, argsMap, new Callback<String>() { @Override public void onResponse(Call<String> call, final Response<String> response) { CommonUtils.runOnUIThread(new Runnable() { @Override public void run() { //将数据抽象出去 setResultData(response.body()); } }); //将数据写入本地 writeDataToLocal(baseUrl + path + keyValues, validTime, response.body()); } @Override public void onFailure(Call<String> call, Throwable t) { ToastUtil.show(MyApplication.getContext(), "请求出错,代码" + t.getMessage()); setResulttError(ShowingPager.STATE_LOAD_ERROR); } }); } private String getDataFromLocal(String path, int validTime) { //读取文件信息 //读时间 try { File file = new File(fileDir, Md5.Md5(path)); BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); String s = bufferedReader.readLine(); long time = Long.parseLong(s); //和当前时间进行比较 //111-110 if (System.currentTimeMillis() < time) { //将信息读出来 StringBuilder builder = new StringBuilder(); String line = null; while ((line = bufferedReader.readLine()) != null) { builder.append(line); } bufferedReader.close(); return builder.toString(); } else { //无效 return null; } } catch (Exception e) { e.printStackTrace(); } return null; } public abstract void setResultData(String data); public abstract void setResulttError(int state); /** * 将数据写到本地 * * @param path * @param * @param validTime * @param */ private void writeDataToLocal(String path, int validTime, String data) { //每一条请求,都是生成一个文件 dawedfakwehfaowehfoaw try { File file = new File(fileDir, Md5.Md5(path)); //写流信息 BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file)); bufferedWriter.write(System.currentTimeMillis() + validTime + "\r\n"); //从网络上请求的数据 bufferedWriter.write(data); bufferedWriter.close(); } catch (Exception e) { e.printStackTrace(); } }}
BaseFragment
package history.six.com.rushingdemo.base;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import history.six.com.rushingdemo.interfaces.IResetShowingPageListener;import history.six.com.rushingdemo.utils.NetUtils;import history.six.com.rushingdemo.view.ShowingPager;public abstract class BaseFragment extends Fragment { public ShowingPager showingPager; //底部标签Fragment的父类,学会设定抽象方法 @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { showingPager = new ShowingPager(getActivity()) { @Override public View setSuccessView() { return setBaseSuccessView(); } @Override public void setTitleView(View titleView) { setBaseTitleView(titleView); } @Override public boolean needTitleView() { return isNeedTitle(); } }; return showingPager; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); showingPager.setIResetShowingPageListener(new IResetShowingPageListener() { @Override public void onResetClick(View view) { onLoad(); } }); onLoad(); } public abstract void onLoad(); public abstract View setBaseSuccessView(); protected abstract boolean isNeedTitle(); public abstract void setBaseTitleView(View v);}
FragmentFactory
package history.six.com.rushingdemo.factory;import android.support.v4.app.Fragment;import java.util.HashMap;import history.six.com.rushingdemo.fragment.CateGoryFragment;import history.six.com.rushingdemo.fragment.DuoBaoFragment;import history.six.com.rushingdemo.fragment.MineFragment;import history.six.com.rushingdemo.fragment.NewJieXiaoFragment;import history.six.com.rushingdemo.fragment.OrderFragment;public class FragmentFactory { //Fragment的管理类,运用工厂模式 //创建集合 private static HashMap<String, Fragment> fragmentHashMap = new HashMap<>(); //创建静态方法 public static Fragment getFragment(String title) { Fragment fragment = fragmentHashMap.get(title); if (fragment != null) { return fragment; } switch (title){ case "夺宝": fragment = new DuoBaoFragment(); break; case "分类": fragment = new CateGoryFragment(); break; case "最新揭晓": fragment = new NewJieXiaoFragment(); break; case "订单": fragment = new OrderFragment(); break; case "我的": fragment = new MineFragment(); break; } fragmentHashMap.put(title, fragment); return fragment; }}
5个底部标签Fragment都继承BaseFragment ,除了“我的界面”没有网络加载判断不用外,继承Fragment
package history.six.com.rushingdemo.fragment;import android.graphics.Color;import android.support.v7.widget.RecyclerView;import android.view.View;import android.widget.TextView;import android.widget.Toast;import com.youth.banner.Banner;import history.six.com.rushingdemo.R;import history.six.com.rushingdemo.base.BaseData;import history.six.com.rushingdemo.base.BaseFragment;import history.six.com.rushingdemo.interfaces.IResetShowingPageListener;import history.six.com.rushingdemo.view.ShowingPager;public class DuoBaoFragment extends BaseFragment { private Banner banner; private RecyclerView mDuoBaoRecyclerView; /** * 加载成功视图 * @return */ @Override public View setBaseSuccessView() { View duobao_fragment = View.inflate(getActivity(), R.layout.duobao_fragment,null); banner = (Banner) duobao_fragment.findViewById(R.id.banner); mDuoBaoRecyclerView = (RecyclerView) duobao_fragment.findViewById(R.id.duoBaoRecyclerView); return duobao_fragment; } /** * 设置标题是否需要隐藏 * @return */ @Override protected boolean isNeedTitle() { return true; } /** * 设置标题 * @param v */ @Override public void setBaseTitleView(View v) { TextView middleTitle_tv = (TextView) v.findViewById(R.id.middleTitle_tv); middleTitle_tv.setText("夺宝"); middleTitle_tv.setTextSize(20); middleTitle_tv.setTextColor(Color.WHITE); } /** * 加载数据 */ @Override public void onLoad() { showingPager.setIResetShowingPageListener(new IResetShowingPageListener() { @Override public void onResetClick(View view) { Toast.makeText(getActivity(), "点击重新加载", Toast.LENGTH_SHORT).show(); } }); new BaseData() { @Override public void setResultData(String data) { showingPager.setCurrentState(ShowingPager.StateType.STATE_LOAD_SUCCESS); } @Override public void setResulttError(int state) { showingPager.setCurrentState(ShowingPager.StateType.STATE_LOAD_ERROR); } }.getData("www.baidu.com","",BaseData.NOTIME); }}
接口包
**1. IResetShowingPageListener
2. ReadCookiesInterceptor
3. RetrofitAPI
4. SaveCookiesInterceptor**
IResetShowingPageListener##
package history.six.com.rushingdemo.interfaces;import android.view.View;public interface IResetShowingPageListener { public void onResetClick(View view);}
ReadCookiesInterceptor
package history.six.com.rushingdemo.interfaces;import java.io.IOException;import history.six.com.rushingdemo.utils.CommonUtils;import okhttp3.Interceptor;import okhttp3.Request;public class ReadCookiesInterceptor implements Interceptor { @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request.Builder builder = chain.request().newBuilder(); String cookie = CommonUtils.getString("cookie"); //将cookie添加到请求头中 builder.addHeader("Cookie", cookie); return chain.proceed(builder.build()); }}
RetrofitAPI
package history.six.com.rushingdemo.interfaces;import java.util.Map;import retrofit2.Call;import retrofit2.http.FieldMap;import retrofit2.http.FormUrlEncoded;import retrofit2.http.GET;import retrofit2.http.POST;import retrofit2.http.Url;public interface RetrofitAPI { @GET Call<String> getMethod(@Url String url); @FormUrlEncoded @POST Call<String> postMothod(@Url String url, @FieldMap Map<String, String> map);}
SaveCookiesInterceptor
package history.six.com.rushingdemo.interfaces;import android.util.Log;import java.io.IOException;import history.six.com.rushingdemo.utils.CommonUtils;import okhttp3.Interceptor;import okhttp3.Response;public class SaveCookiesInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); //PHPSESSID=vg6d8mpgmqgni6ct15skcjjm71;loginname=15330276178; StringBuilder stringBuilder=new StringBuilder(); if (!originalResponse.headers("Set-Cookie").isEmpty()) { for (String header : originalResponse.headers("Set-Cookie")) { Log.i("AAAA----","=="+header+"=="); String cookie = header.substring(0, header.indexOf(";") + 1); stringBuilder.append(cookie); } } Log.i("AAAA","++"+stringBuilder.toString()+"++"); CommonUtils.saveString("cookie",stringBuilder.toString()); return originalResponse; }}
manager包
HttpMangerUtils
package history.six.com.rushingdemo.manager;import android.util.Log;import java.util.Map;import java.util.concurrent.TimeUnit;import history.six.com.rushingdemo.interfaces.ReadCookiesInterceptor;import history.six.com.rushingdemo.interfaces.RetrofitAPI;import history.six.com.rushingdemo.interfaces.SaveCookiesInterceptor;import okhttp3.OkHttpClient;import retrofit2.Call;import retrofit2.Callback;import retrofit2.Response;import retrofit2.Retrofit;import retrofit2.converter.scalars.ScalarsConverterFactory;public class HttpMangerUtils { private static final int DEFAULT_TIMEOUT = 5; /** * get请求方式 * * @param baseUrl * @param url * @param callback */ public static void getData(String baseUrl, String url, final Callback<String> callback) { OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .build(); Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl).addConverterFactory(ScalarsConverterFactory.create()).client(client).build(); RetrofitAPI projectAPI = retrofit.create(RetrofitAPI.class); Call<String> call = projectAPI.getMethod(url); call.enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, Response<String> response) { callback.onResponse(call, response); } @Override public void onFailure(Call<String> call, Throwable t) { callback.onFailure(call, t); } }); } /** * post请求方式 * * @param baseUrl * @param url * @param map * @param callback */ public static void postMethod(boolean isReadCookie, boolean isSaveCookie, String baseUrl, String url, Map<String, String> map, final Callback<String> callback) { OkHttpClient httpClient = null; if (isReadCookie && !isSaveCookie) { httpClient = new OkHttpClient.Builder() .addInterceptor(new ReadCookiesInterceptor()).connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .build(); Log.i("AAA", "只读不写"); } if (isSaveCookie && !isReadCookie) { httpClient = new OkHttpClient.Builder() .addInterceptor(new SaveCookiesInterceptor()).connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .build(); Log.i("AAA", "只写不读"); } if (isSaveCookie && isReadCookie) { httpClient = new OkHttpClient.Builder().connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .addInterceptor(new SaveCookiesInterceptor()).addInterceptor(new ReadCookiesInterceptor()) .build(); Log.i("AAA", "有些有毒"); } if (!isSaveCookie && !isReadCookie) { httpClient = new OkHttpClient.Builder().connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) .build(); Log.i("AAA", "不写不读"); } Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl).client(httpClient).addConverterFactory(ScalarsConverterFactory.create()).build(); RetrofitAPI projectAPI = retrofit.create(RetrofitAPI.class); Call<String> call = projectAPI.postMothod(url, map); call.enqueue(new Callback<String>() { @Override public void onResponse(Call<String> call, Response<String> response) { callback.onResponse(call, response); } @Override public void onFailure(Call<String> call, Throwable t) { callback.onFailure(call, t); } }); }}
BannerImageLoader
package history.six.com.rushingdemo.utils;import android.content.Context;import android.widget.ImageView;import com.bumptech.glide.Glide;import com.youth.banner.loader.ImageLoader;public class BannerImageLoader extends ImageLoader { @Override public void displayImage(Context context, Object path, ImageView imageView) { Glide.with(context).load(path).into(imageView); }}
CommonUtils
package history.six.com.rushingdemo.utils;import android.content.SharedPreferences;import android.graphics.drawable.Drawable;import android.view.View;import history.six.com.rushingdemo.application.MyApplication;public class CommonUtils { public static final String TAG = "NEWSTOP"; private static SharedPreferences sharedPreferences; private static String SP_NAME = "YYDB"; //打气 public static View inflter(int loyoutId) { View view = View.inflate(MyApplication.getContext(), loyoutId, null); return view; } /** * dip 相对像素转换为 px绝对像素 * * @param dip * @return */ public static int dip2px(int dip) { //获取像素密度 float density = MyApplication.getContext().getResources().getDisplayMetrics().density; //转换 int px = (int) (dip * density + 0.5f); return px; } /** * px 相对像素转换为 dip 绝对像素 * * @param px * @return */ public static int px2dip(int px) { //获取像素密度 float density = MyApplication.getContext().getResources().getDisplayMetrics().density; //转换 int dip = (int) (px / density + 0.5f); return dip; } //获取资源文件中字符串 public static String getString(int stringId) { String string = MyApplication.getContext().getResources().getString(stringId); return string; } //获取资源文件的drawable文件 public static Drawable getDrawable(int did) { return MyApplication.getContext().getResources().getDrawable(did); } //清空之前保存的titles数据 public static void removeSp(String flag){ if (sharedPreferences == null) { sharedPreferences = MyApplication.getContext().getSharedPreferences(TAG, MyApplication.getContext().MODE_PRIVATE); } SharedPreferences.Editor edit = sharedPreferences.edit(); edit.remove(flag); edit.commit(); } //判断当前任务是否在主线程中,如果在主线程中直接执行,否则通过handler执行 public static void runOnUIThread(Runnable runnable) { if (android.os.Process.myTid() == MyApplication.getMainThreadId()) { runnable.run(); } else { MyApplication.getHandler().post(runnable); } } //使用线程池执行runnable public static void executeRunnable(Runnable runnable) { MyApplication.getThreadPoll().execute(runnable); } public static void saveString(String key, String value) { if (sharedPreferences == null) sharedPreferences = MyApplication.getContext().getSharedPreferences(SP_NAME, MyApplication.getContext().MODE_PRIVATE); sharedPreferences.edit().putString(key, value).commit(); } //获取String 的 sp public static String getString(String str) { if (sharedPreferences == null) { sharedPreferences = MyApplication.getContext().getSharedPreferences(SP_NAME, MyApplication.getContext().MODE_PRIVATE); } return sharedPreferences.getString(str, null); } //保存 Boolean 的 sp public static void saveBolean(String str, boolean flag) { if (sharedPreferences == null) { sharedPreferences = MyApplication.getContext().getSharedPreferences(SP_NAME, MyApplication.getContext().MODE_PRIVATE); } SharedPreferences.Editor edit = sharedPreferences.edit(); edit.putBoolean(str, flag); edit.commit(); } //获取 Boolean的sp public static boolean getBoolean(String str) { if (sharedPreferences == null) { sharedPreferences = MyApplication.getContext().getSharedPreferences(SP_NAME, MyApplication.getContext().MODE_PRIVATE); } return sharedPreferences.getBoolean(str, false); }}
LogUtils
package history.six.com.rushingdemo.utils;import android.util.Log;public class LogUtils { public static final boolean isDebug=true; public static void i(String TAG, String info){ if(isDebug){ Log.i(TAG,info); } } public static void d(String TAG, String info){ if(isDebug){ Log.d(TAG,info); } } public static void e(String TAG, String info){ if(isDebug){ Log.e(TAG,info); } }}
Md5
package history.six.com.rushingdemo.utils;import java.security.MessageDigest;public class Md5 { private static StringBuffer buf; public static String Md5(String plainText ) { try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(plainText.getBytes()); byte b[] = md.digest(); int i; buf = new StringBuffer(""); for (int offset = 0; offset < b.length; offset++) { i = b[offset]; if(i<0) i+= 256; if(i<16) buf.append("0"); buf.append(Integer.toHexString(i)); } //System.out.println("result: " + buf.toString());//32位的加密 //System.out.println("result: " + buf.toString().substring(8,24));//16位的加密 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return buf.toString(); } }
MyGlideMode
package history.six.com.rushingdemo.utils;import android.content.Context;import android.os.Environment;import com.bumptech.glide.Glide;import com.bumptech.glide.GlideBuilder;import com.bumptech.glide.load.engine.cache.DiskLruCacheFactory;import com.bumptech.glide.load.engine.cache.LruResourceCache;import com.bumptech.glide.module.GlideModule;import java.io.File;public class MyGlideMode implements GlideModule { /**使用方法 * Glide.with(WaterWallActivity.this) .load(加载路径) .placeholder(默认加载) .error(加载错误) .into(控件); * @param context * @param builder */ @Override public void applyOptions(Context context, GlideBuilder builder) { int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取系统分配给应用的总内存大小 int memoryCacheSize = maxMemory / 8;//设置图片内存缓存占用八分之一 //设置内存缓存大小 builder.setMemoryCache(new LruResourceCache(memoryCacheSize)); //指定到SDcard File cacheDir = Environment.getExternalStorageDirectory();//指定的是数据的缓存地址 int diskCacheSize = 1024 * 1024 * 30;//最多可以缓存多少字节的数据 //设置磁盘缓存大小 builder.setDiskCache(new DiskLruCacheFactory(cacheDir.getPath(), "glide", diskCacheSize)); } @Override public void registerComponents(Context context, Glide glide) { }}
NetUtils
package history.six.com.rushingdemo.utils;import android.content.Context;import android.net.ConnectivityManager;import android.net.NetworkInfo;import android.telephony.TelephonyManager;import android.text.TextUtils;import history.six.com.rushingdemo.application.MyApplication;public class NetUtils { public static boolean isHaveNet() { if (getNetWorkType(MyApplication.getContext()) == NETWORKTYPE_INVALID) { return false; } else { return true; } } /** * 没有网络 */ public static final int NETWORKTYPE_INVALID = 0; /** * wap网络 */ public static final int NETWORKTYPE_WAP = 1; /** * 2G网络 */ public static final int NETWORKTYPE_2G = 2; /** * 3G和3G以上网络,或统称为快速网络 */ public static final int NETWORKTYPE_3G = 3; /** * wifi网络 */ public static final int NETWORKTYPE_WIFI = 4; private static int mNetWorkType; /** * LogUtils.java * 获取网络状态,wifi,wap,2g,3g. * * @param context 上下文 * @return int 网络状态 {@link #NETWORKTYPE_2G},{@link #NETWORKTYPE_3G}, * * {@link #NETWORKTYPE_INVALID},{@link #NETWORKTYPE_WAP}* * <p> * {@link #NETWORKTYPE_WIFI} */ public static boolean isFastMobileNetwork(Context context) { TelephonyManager telephonyManager = (TelephonyManager) context .getSystemService(Context.TELEPHONY_SERVICE); switch (telephonyManager.getNetworkType()) { case TelephonyManager.NETWORK_TYPE_1xRTT: return false; // ~ 50-100 kbps case TelephonyManager.NETWORK_TYPE_CDMA: return false; // ~ 14-64 kbps case TelephonyManager.NETWORK_TYPE_EDGE: return false; // ~ 50-100 kbps case TelephonyManager.NETWORK_TYPE_EVDO_0: return true; // ~ 400-1000 kbps case TelephonyManager.NETWORK_TYPE_EVDO_A: return true; // ~ 600-1400 kbps case TelephonyManager.NETWORK_TYPE_GPRS: return false; // ~ 100 kbps case TelephonyManager.NETWORK_TYPE_HSDPA: return true; // ~ 2-14 Mbps case TelephonyManager.NETWORK_TYPE_HSPA: return true; // ~ 700-1700 kbps case TelephonyManager.NETWORK_TYPE_HSUPA: return true; // ~ 1-23 Mbps case TelephonyManager.NETWORK_TYPE_UMTS: return true; // ~ 400-7000 kbps case TelephonyManager.NETWORK_TYPE_EHRPD: return true; // ~ 1-2 Mbps case TelephonyManager.NETWORK_TYPE_EVDO_B: return true; // ~ 5 Mbps case TelephonyManager.NETWORK_TYPE_HSPAP: return true; // ~ 10-20 Mbps case TelephonyManager.NETWORK_TYPE_IDEN: return false; // ~25 kbps case TelephonyManager.NETWORK_TYPE_LTE: return true; // ~ 10+ Mbps case TelephonyManager.NETWORK_TYPE_UNKNOWN: return false; default: return false; } } public static int getNetWorkType(Context context) { String str = null; ConnectivityManager manager = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = manager.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { String type = networkInfo.getTypeName(); if (type.equalsIgnoreCase("WIFI")) { mNetWorkType = NETWORKTYPE_WIFI; } else if (type.equalsIgnoreCase("MOBILE")) { String proxyHost = android.net.Proxy.getDefaultHost(); mNetWorkType = TextUtils.isEmpty(proxyHost) ? (isFastMobileNetwork(context) ? NETWORKTYPE_3G : NETWORKTYPE_2G) : NETWORKTYPE_WAP; } } else { mNetWorkType = NETWORKTYPE_INVALID; } return mNetWorkType; }}
PermissionUtils
package history.six.com.rushingdemo.utils;import android.annotation.TargetApi;import android.app.Activity;import android.content.Context;import android.content.pm.PackageManager;import android.os.Build;import android.support.v4.content.ContextCompat;import java.util.ArrayList;import java.util.List;public class PermissionUtils { //权限工具类 private static int mRequestCode = -1; private static OnPermissionListener mOnPermissionListener; public interface OnPermissionListener { void onPermissionGranted(); void onPermissionDenied(); } @TargetApi(Build.VERSION_CODES.M) public static void requestPermissions(Context context, int requestCode , String[] permissions, OnPermissionListener listener) { if (context instanceof Activity) { mOnPermissionListener = listener; List<String> deniedPermissions = getDeniedPermissions(context, permissions); if (deniedPermissions.size() > 0) { mRequestCode = requestCode; ((Activity) context).requestPermissions(deniedPermissions .toArray(new String[deniedPermissions.size()]), requestCode); } else { if (mOnPermissionListener != null) mOnPermissionListener.onPermissionGranted(); } } else { throw new RuntimeException("Context must be an Activity"); } } /** * 获取请求权限中需要授权的权限 */ private static List<String> getDeniedPermissions(Context context, String... permissions) { List<String> deniedPermissions = new ArrayList<>(); for (String permission : permissions) { if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED) { deniedPermissions.add(permission); } } return deniedPermissions; } /** * 请求权限结果,对应Activity中onRequestPermissionsResult()方法。 */ public static void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (mRequestCode != -1 && requestCode == mRequestCode) { if (mOnPermissionListener != null) { if (verifyPermissions(grantResults)) { mOnPermissionListener.onPermissionGranted(); } else { mOnPermissionListener.onPermissionDenied(); } } } } /** * 验证所有权限是否都已经授权 */ private static boolean verifyPermissions(int[] grantResults) { for (int grantResult : grantResults) { if (grantResult != PackageManager.PERMISSION_GRANTED) { return false; } } return true; }}
ToastUtil
package history.six.com.rushingdemo.utils;import android.content.Context;import android.widget.Toast;public class ToastUtil { public static void show(Context context, String content) { Toast.makeText(context, content, Toast.LENGTH_SHORT).show(); } public static void showLong(Context context, int contentId) { Toast.makeText(context, contentId, Toast.LENGTH_LONG).show(); }}
view包
LazyViewPager
package history.six.com.rushingdemo.view;//viewpage禁止预加载父类/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */import android.content.Context;import android.database.DataSetObserver;import android.graphics.Canvas;import android.graphics.Rect;import android.graphics.drawable.Drawable;import android.os.Parcel;import android.os.Parcelable;import android.os.SystemClock;import android.support.v4.os.ParcelableCompat;import android.support.v4.os.ParcelableCompatCreatorCallbacks;import android.support.v4.view.KeyEventCompat;import android.support.v4.view.MotionEventCompat;import android.support.v4.view.PagerAdapter;import android.support.v4.view.VelocityTrackerCompat;import android.support.v4.view.ViewCompat;import android.support.v4.view.ViewConfigurationCompat;import android.support.v4.widget.EdgeEffectCompat;import android.util.AttributeSet;import android.util.Log;import android.view.FocusFinder;import android.view.KeyEvent;import android.view.MotionEvent;import android.view.SoundEffectConstants;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.view.ViewParent;import android.view.accessibility.AccessibilityEvent;import android.view.animation.Interpolator;import android.widget.Scroller;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;/** * Layout manager that allows the user to flip left and right * through pages of data. You supply an implementation of a * {@link PagerAdapter} to generate the pages that the view shows. * <p> * <p>Note this class is currently under early design and * development. The API will likely change in later updates of * the compatibility library, requiring changes to the source code * of apps when they are compiled against the newer version.</p> */public class LazyViewPager extends ViewGroup { private static final String TAG = "LazyViewPager"; private static final boolean DEBUG = false; private static final boolean USE_CACHE = false; //设置默认加载的页数,可以灵活进行设置 private static final int DEFAULT_OFFSCREEN_PAGES = 0; private static final int MAX_SETTLE_DURATION = 600; static class ItemInfo { Object object; int position; boolean scrolling; } private static final Comparator<ItemInfo> COMPARATOR = new Comparator<ItemInfo>() { @Override public int compare(ItemInfo lhs, ItemInfo rhs) { return lhs.position - rhs.position; } }; private static final Interpolator sInterpolator = new Interpolator() { public float getInterpolation(float t) { // _o(t) = t * t * ((tension + 1) * t + tension) // o(t) = _o(t - 1) + 1 t -= 1.0f; return t * t * t + 1.0f; } }; private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>(); private PagerAdapter mAdapter; private int mCurItem; // Index of currently displayed page. private int mRestoredCurItem = -1; private Parcelable mRestoredAdapterState = null; private ClassLoader mRestoredClassLoader = null; private Scroller mScroller; private PagerObserver mObserver; private int mPageMargin; private Drawable mMarginDrawable; private int mChildWidthMeasureSpec; private int mChildHeightMeasureSpec; private boolean mInLayout; private boolean mScrollingCacheEnabled; private boolean mPopulatePending; private boolean mScrolling; private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES; private boolean mIsBeingDragged; private boolean mIsUnableToDrag; private int mTouchSlop; private float mInitialMotionX; /** * Position of the last motion event. */ private float mLastMotionX; private float mLastMotionY; /** * ID of the active pointer. This is used to retain consistency during * drags/flings if multiple pointers are used. */ private int mActivePointerId = INVALID_POINTER; /** * Sentinel value for no current active pointer. * Used by {@link #mActivePointerId}. */ private static final int INVALID_POINTER = -1; /** * Determines speed during touch scrolling */ private VelocityTracker mVelocityTracker; private int mMinimumVelocity; private int mMaximumVelocity; private float mBaseLineFlingVelocity; private float mFlingVelocityInfluence; private boolean mFakeDragging; private long mFakeDragBeginTime; private EdgeEffectCompat mLeftEdge; private EdgeEffectCompat mRightEdge; private boolean mFirstLayout = true; private OnPageChangeListener mOnPageChangeListener; /** * Indicates that the pager is in an idle, settled state. The current page * is fully in view and no animation is in progress. */ public static final int SCROLL_STATE_IDLE = 0; /** * Indicates that the pager is currently being dragged by the user. */ public static final int SCROLL_STATE_DRAGGING = 1; /** * Indicates that the pager is in the process of settling to a final position. */ public static final int SCROLL_STATE_SETTLING = 2; private int mScrollState = SCROLL_STATE_IDLE; /** * Callback interface for responding to changing state of the selected page. */ public interface OnPageChangeListener { /** * This method will be invoked when the current page is scrolled, either as part * of a programmatically initiated smooth scroll or a user initiated touch scroll. * * @param position Position index of the first page currently being displayed. * Page position+1 will be visible if positionOffset is nonzero. * @param positionOffset Value from [0, 1) indicating the offset from the page at position. * @param positionOffsetPixels Value in pixels indicating the offset from position. */ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); /** * This method will be invoked when a new page becomes selected. Animation is not * necessarily complete. * * @param position Position index of the new selected page. */ public void onPageSelected(int position); /** * Called when the scroll state changes. Useful for discovering when the user * begins dragging, when the pager is automatically settling to the current page, * or when it is fully stopped/idle. * * @param state The new scroll state. * @see android.support.v4.view.ViewPager#SCROLL_STATE_IDLE * @see android.support.v4.view.ViewPager#SCROLL_STATE_DRAGGING * @see android.support.v4.view.ViewPager#SCROLL_STATE_SETTLING */ public void onPageScrollStateChanged(int state); } public static class SimpleOnPageChangeListener implements OnPageChangeListener { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // This space for rent } @Override public void onPageSelected(int position) { // This space for rent } @Override public void onPageScrollStateChanged(int state) { // This space for rent } } public LazyViewPager(Context context) { super(context); initViewPager(); } public LazyViewPager(Context context, AttributeSet attrs) { super(context, attrs); initViewPager(); } void initViewPager() { setWillNotDraw(false); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setFocusable(true); final Context context = getContext(); mScroller = new Scroller(context, sInterpolator); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); mLeftEdge = new EdgeEffectCompat(context); mRightEdge = new EdgeEffectCompat(context); float density = context.getResources().getDisplayMetrics().density; mBaseLineFlingVelocity = 2500.0f * density; mFlingVelocityInfluence = 0.4f; } private void setScrollState(int newState) { if (mScrollState == newState) { return; } mScrollState = newState; if (mOnPageChangeListener != null) { mOnPageChangeListener.onPageScrollStateChanged(newState); } } public void setAdapter(PagerAdapter adapter) { if (mAdapter != null) {// mAdapter.unregisterDataSetObserver(mObserver); mAdapter.startUpdate(this); for (int i = 0; i < mItems.size(); i++) { final ItemInfo ii = mItems.get(i); mAdapter.destroyItem(this, ii.position, ii.object); } mAdapter.finishUpdate(this); mItems.clear(); removeAllViews(); mCurItem = 0; scrollTo(0, 0); } mAdapter = adapter; if (mAdapter != null) { if (mObserver == null) { mObserver = new PagerObserver(); }// mAdapter.registerDataSetObserver(mObserver); mPopulatePending = false; if (mRestoredCurItem >= 0) { mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader); setCurrentItemInternal(mRestoredCurItem, false, true); mRestoredCurItem = -1; mRestoredAdapterState = null; mRestoredClassLoader = null; } else { populate(); } } } public PagerAdapter getAdapter() { return mAdapter; } /** * Set the currently selected page. If the ViewPager has already been through its first * layout there will be a smooth animated transition between the current item and the * specified item. * * @param item Item index to select */ public void setCurrentItem(int item) { mPopulatePending = false; setCurrentItemInternal(item, !mFirstLayout, false); } /** * Set the currently selected page. * * @param item Item index to select * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately */ public void setCurrentItem(int item, boolean smoothScroll) { mPopulatePending = false; setCurrentItemInternal(item, smoothScroll, false); } public int getCurrentItem() { return mCurItem; } void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) { setCurrentItemInternal(item, smoothScroll, always, 0); } void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) { if (mAdapter == null || mAdapter.getCount() <= 0) { setScrollingCacheEnabled(false); return; } if (!always && mCurItem == item && mItems.size() != 0) { setScrollingCacheEnabled(false); return; } if (item < 0) { item = 0; } else if (item >= mAdapter.getCount()) { item = mAdapter.getCount() - 1; } final int pageLimit = mOffscreenPageLimit; if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) { // We are doing a jump by more than one page. To avoid // glitches, we want to keep all current pages in the view // until the scroll ends. for (int i = 0; i < mItems.size(); i++) { mItems.get(i).scrolling = true; } } final boolean dispatchSelected = mCurItem != item; mCurItem = item; populate(); final int destX = (getWidth() + mPageMargin) * item; if (smoothScroll) { smoothScrollTo(destX, 0, velocity); if (dispatchSelected && mOnPageChangeListener != null) { mOnPageChangeListener.onPageSelected(item); } } else { if (dispatchSelected && mOnPageChangeListener != null) { mOnPageChangeListener.onPageSelected(item); } completeScroll(); scrollTo(destX, 0); } } public void setOnPageChangeListener(OnPageChangeListener listener) { mOnPageChangeListener = listener; } /** * Returns the number of pages that will be retained to either side of the * current page in the view hierarchy in an idle state. Defaults to 1. * * @return How many pages will be kept offscreen on either side * @see #setOffscreenPageLimit(int) */ public int getOffscreenPageLimit() { return mOffscreenPageLimit; } /** * Set the number of pages that should be retained to either side of the * current page in the view hierarchy in an idle state. Pages beyond this * limit will be recreated from the adapter when needed. * <p> * <p>This is offered as an optimization. If you know in advance the number * of pages you will need to support or have lazy-loading mechanisms in place * on your pages, tweaking this setting can have benefits in perceived smoothness * of paging animations and interaction. If you have a small number of pages (3-4) * that you can keep active all at once, less time will be spent in layout for * newly created view subtrees as the user pages back and forth.</p> * <p> * <p>You should keep this limit low, especially if your pages have complex layouts. * This setting defaults to 1.</p> * * @param limit How many pages will be kept offscreen in an idle state. */ public void setOffscreenPageLimit(int limit) { if (limit < DEFAULT_OFFSCREEN_PAGES) { Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + DEFAULT_OFFSCREEN_PAGES); limit = DEFAULT_OFFSCREEN_PAGES; } if (limit != mOffscreenPageLimit) { mOffscreenPageLimit = limit; populate(); } } /** * Set the margin between pages. * * @param marginPixels Distance between adjacent pages in pixels * @see #getPageMargin() * @see #setPageMarginDrawable(Drawable) * @see #setPageMarginDrawable(int) */ public void setPageMargin(int marginPixels) { final int oldMargin = mPageMargin; mPageMargin = marginPixels; final int width = getWidth(); recomputeScrollPosition(width, width, marginPixels, oldMargin); requestLayout(); } /** * Return the margin between pages. * * @return The size of the margin in pixels */ public int getPageMargin() { return mPageMargin; } /** * Set a drawable that will be used to fill the margin between pages. * * @param d Drawable to display between pages */ public void setPageMarginDrawable(Drawable d) { mMarginDrawable = d; if (d != null) refreshDrawableState(); setWillNotDraw(d == null); invalidate(); } /** * Set a drawable that will be used to fill the margin between pages. * * @param resId Resource ID of a drawable to display between pages */ public void setPageMarginDrawable(int resId) { setPageMarginDrawable(getContext().getResources().getDrawable(resId)); } @Override protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || who == mMarginDrawable; } @Override protected void drawableStateChanged() { super.drawableStateChanged(); final Drawable d = mMarginDrawable; if (d != null && d.isStateful()) { d.setState(getDrawableState()); } } // We want the duration of the page snap animation to be influenced by the distance that // the screen has to travel, however, we don't want this duration to be effected in a // purely linear fashion. Instead, we use this method to moderate the effect that the distance // of travel has on the overall snap duration. float distanceInfluenceForSnapDuration(float f) { f -= 0.5f; // center the values about 0. f *= 0.3f * Math.PI / 2.0f; return (float) Math.sin(f); } /** * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. * * @param x the number of pixels to scroll by on the X axis * @param y the number of pixels to scroll by on the Y axis */ void smoothScrollTo(int x, int y) { smoothScrollTo(x, y, 0); } /** * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. * * @param x the number of pixels to scroll by on the X axis * @param y the number of pixels to scroll by on the Y axis * @param velocity the velocity associated with a fling, if applicable. (0 otherwise) */ void smoothScrollTo(int x, int y, int velocity) { if (getChildCount() == 0) { // Nothing to do. setScrollingCacheEnabled(false); return; } int sx = getScrollX(); int sy = getScrollY(); int dx = x - sx; int dy = y - sy; if (dx == 0 && dy == 0) { completeScroll(); setScrollState(SCROLL_STATE_IDLE); return; } setScrollingCacheEnabled(true); mScrolling = true; setScrollState(SCROLL_STATE_SETTLING); final float pageDelta = (float) Math.abs(dx) / (getWidth() + mPageMargin); int duration = (int) (pageDelta * 100); velocity = Math.abs(velocity); if (velocity > 0) { duration += (duration / (velocity / mBaseLineFlingVelocity)) * mFlingVelocityInfluence; } else { duration += 100; } duration = Math.min(duration, MAX_SETTLE_DURATION); mScroller.startScroll(sx, sy, dx, dy, duration); invalidate(); } void addNewItem(int position, int index) { ItemInfo ii = new ItemInfo(); ii.position = position; ii.object = mAdapter.instantiateItem(this, position); if (index < 0) { mItems.add(ii); } else { mItems.add(index, ii); } } void dataSetChanged() { // This method only gets called if our observer is attached, so mAdapter is non-null. boolean needPopulate = mItems.size() < 3 && mItems.size() < mAdapter.getCount(); int newCurrItem = -1; for (int i = 0; i < mItems.size(); i++) { final ItemInfo ii = mItems.get(i); final int newPos = mAdapter.getItemPosition(ii.object); if (newPos == PagerAdapter.POSITION_UNCHANGED) { continue; } if (newPos == PagerAdapter.POSITION_NONE) { mItems.remove(i); i--; mAdapter.destroyItem(this, ii.position, ii.object); needPopulate = true; if (mCurItem == ii.position) { // Keep the current item in the valid range newCurrItem = Math.max(0, Math.min(mCurItem, mAdapter.getCount() - 1)); } continue; } if (ii.position != newPos) { if (ii.position == mCurItem) { // Our current item changed position. Follow it. newCurrItem = newPos; } ii.position = newPos; needPopulate = true; } } Collections.sort(mItems, COMPARATOR); if (newCurrItem >= 0) { // TODO This currently causes a jump. setCurrentItemInternal(newCurrItem, false, true); needPopulate = true; } if (needPopulate) { populate(); requestLayout(); } } void populate() { if (mAdapter == null) { return; } // Bail now if we are waiting to populate. This is to hold off // on creating views from the time the user releases their finger to // fling to a new position until we have finished the scroll to // that position, avoiding glitches from happening at that point. if (mPopulatePending) { if (DEBUG) Log.i(TAG, "populate is pending, skipping for now..."); return; } // Also, don't populate until we are attached to a window. This is to // avoid trying to populate before we have restored our view hierarchy // state and conflicting with what is restored. if (getWindowToken() == null) { return; } mAdapter.startUpdate(this); final int pageLimit = mOffscreenPageLimit; final int startPos = Math.max(0, mCurItem - pageLimit); final int N = mAdapter.getCount(); final int endPos = Math.min(N - 1, mCurItem + pageLimit); if (DEBUG) Log.v(TAG, "populating: startPos=" + startPos + " endPos=" + endPos); // Add and remove pages in the existing list. int lastPos = -1; for (int i = 0; i < mItems.size(); i++) { ItemInfo ii = mItems.get(i); if ((ii.position < startPos || ii.position > endPos) && !ii.scrolling) { if (DEBUG) Log.i(TAG, "removing: " + ii.position + " @ " + i); mItems.remove(i); i--; mAdapter.destroyItem(this, ii.position, ii.object); } else if (lastPos < endPos && ii.position > startPos) { // The next item is outside of our range, but we have a gap // between it and the last item where we want to have a page // shown. Fill in the gap. lastPos++; if (lastPos < startPos) { lastPos = startPos; } while (lastPos <= endPos && lastPos < ii.position) { if (DEBUG) Log.i(TAG, "inserting: " + lastPos + " @ " + i); addNewItem(lastPos, i); lastPos++; i++; } } lastPos = ii.position; } // Add any new pages we need at the end. lastPos = mItems.size() > 0 ? mItems.get(mItems.size() - 1).position : -1; if (lastPos < endPos) { lastPos++; lastPos = lastPos > startPos ? lastPos : startPos; while (lastPos <= endPos) { if (DEBUG) Log.i(TAG, "appending: " + lastPos); addNewItem(lastPos, -1); lastPos++; } } if (DEBUG) { Log.i(TAG, "Current page list:"); for (int i = 0; i < mItems.size(); i++) { Log.i(TAG, "#" + i + ": page " + mItems.get(i).position); } } ItemInfo curItem = null; for (int i = 0; i < mItems.size(); i++) { if (mItems.get(i).position == mCurItem) { curItem = mItems.get(i); break; } } mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null); mAdapter.finishUpdate(this); if (hasFocus()) { View currentFocused = findFocus(); ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null; if (ii == null || ii.position != mCurItem) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); ii = infoForChild(child); if (ii != null && ii.position == mCurItem) { if (child.requestFocus(FOCUS_FORWARD)) { break; } } } } } } public static class SavedState extends BaseSavedState { int position; Parcelable adapterState; ClassLoader loader; public SavedState(Parcelable superState) { super(superState); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(position); out.writeParcelable(adapterState, flags); } @Override public String toString() { return "FragmentPager.SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " position=" + position + "}"; } public static final Creator<SavedState> CREATOR = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() { @Override public SavedState createFromParcel(Parcel in, ClassLoader loader) { return new SavedState(in, loader); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }); SavedState(Parcel in, ClassLoader loader) { super(in); if (loader == null) { loader = getClass().getClassLoader(); } position = in.readInt(); adapterState = in.readParcelable(loader); this.loader = loader; } } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.position = mCurItem; if (mAdapter != null) { ss.adapterState = mAdapter.saveState(); } return ss; } @Override public void onRestoreInstanceState(Parcelable state) { if (!(state instanceof SavedState)) { super.onRestoreInstanceState(state); return; } SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); if (mAdapter != null) { mAdapter.restoreState(ss.adapterState, ss.loader); setCurrentItemInternal(ss.position, false, true); } else { mRestoredCurItem = ss.position; mRestoredAdapterState = ss.adapterState; mRestoredClassLoader = ss.loader; } } @Override public void addView(View child, int index, LayoutParams params) { if (mInLayout) { addViewInLayout(child, index, params); child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec); } else { super.addView(child, index, params); } if (USE_CACHE) { if (child.getVisibility() != GONE) { child.setDrawingCacheEnabled(mScrollingCacheEnabled); } else { child.setDrawingCacheEnabled(false); } } } ItemInfo infoForChild(View child) { for (int i = 0; i < mItems.size(); i++) { ItemInfo ii = mItems.get(i); if (mAdapter.isViewFromObject(child, ii.object)) { return ii; } } return null; } ItemInfo infoForAnyChild(View child) { ViewParent parent; while ((parent = child.getParent()) != this) { if (parent == null || !(parent instanceof View)) { return null; } child = (View) parent; } return infoForChild(child); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mFirstLayout = true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // For simple implementation, or internal size is always 0. // We depend on the container to specify the layout size of // our view. We can't really know what it is since we will be // adding and removing different arbitrary views and do not // want the layout to change as this happens. setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec)); // Children are just made to fill our space. mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY); mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY); // Make sure we have created all fragments that we need to have shown. mInLayout = true; populate(); mInLayout = false; // Make sure all children have been properly measured. final int size = getChildCount(); for (int i = 0; i < size; ++i) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child + ": " + mChildWidthMeasureSpec); child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec); } } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // Make sure scroll position is set correctly. if (w != oldw) { recomputeScrollPosition(w, oldw, mPageMargin, mPageMargin); } } private void recomputeScrollPosition(int width, int oldWidth, int margin, int oldMargin) { final int widthWithMargin = width + margin; if (oldWidth > 0) { final int oldScrollPos = getScrollX(); final int oldwwm = oldWidth + oldMargin; final int oldScrollItem = oldScrollPos / oldwwm; final float scrollOffset = (float) (oldScrollPos % oldwwm) / oldwwm; final int scrollPos = (int) ((oldScrollItem + scrollOffset) * widthWithMargin); scrollTo(scrollPos, getScrollY()); if (!mScroller.isFinished()) { // We now return to your regularly scheduled scroll, already in progress. final int newDuration = mScroller.getDuration() - mScroller.timePassed(); mScroller.startScroll(scrollPos, 0, mCurItem * widthWithMargin, 0, newDuration); } } else { int scrollPos = mCurItem * widthWithMargin; if (scrollPos != getScrollX()) { completeScroll(); scrollTo(scrollPos, getScrollY()); } } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mInLayout = true; populate(); mInLayout = false; final int count = getChildCount(); final int width = r - l; for (int i = 0; i < count; i++) { View child = getChildAt(i); ItemInfo ii; if (child.getVisibility() != GONE && (ii = infoForChild(child)) != null) { int loff = (width + mPageMargin) * ii.position; int childLeft = getPaddingLeft() + loff; int childTop = getPaddingTop(); if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth() + "x" + child.getMeasuredHeight()); child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), childTop + child.getMeasuredHeight()); } } mFirstLayout = false; } @Override public void computeScroll() { if (DEBUG) Log.i(TAG, "computeScroll: finished=" + mScroller.isFinished()); if (!mScroller.isFinished()) { if (mScroller.computeScrollOffset()) { if (DEBUG) Log.i(TAG, "computeScroll: still scrolling"); int oldX = getScrollX(); int oldY = getScrollY(); int x = mScroller.getCurrX(); int y = mScroller.getCurrY(); if (oldX != x || oldY != y) { scrollTo(x, y); } if (mOnPageChangeListener != null) { final int widthWithMargin = getWidth() + mPageMargin; final int position = x / widthWithMargin; final int offsetPixels = x % widthWithMargin; final float offset = (float) offsetPixels / widthWithMargin; mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels); } // Keep on drawing until the animation has finished. invalidate(); return; } } // Done with scroll, clean up state. completeScroll(); } private void completeScroll() { boolean needPopulate = mScrolling; if (needPopulate) { // Done with scroll, no longer want to cache view drawing. setScrollingCacheEnabled(false); mScroller.abortAnimation(); int oldX = getScrollX(); int oldY = getScrollY(); int x = mScroller.getCurrX(); int y = mScroller.getCurrY(); if (oldX != x || oldY != y) { scrollTo(x, y); } setScrollState(SCROLL_STATE_IDLE); } mPopulatePending = false; mScrolling = false; for (int i = 0; i < mItems.size(); i++) { ItemInfo ii = mItems.get(i); if (ii.scrolling) { needPopulate = true; ii.scrolling = false; } } if (needPopulate) { populate(); } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { /* * This method JUST determines whether we want to intercept the motion. * If we return true, onMotionEvent will be called and we do the actual * scrolling there. */ final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; // Always take care of the touch gesture being complete. if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { // Release the drag. if (DEBUG) Log.v(TAG, "Intercept done!"); mIsBeingDragged = false; mIsUnableToDrag = false; mActivePointerId = INVALID_POINTER; return false; } // Nothing more to do here if we have decided whether or not we // are dragging. if (action != MotionEvent.ACTION_DOWN) { if (mIsBeingDragged) { if (DEBUG) Log.v(TAG, "Intercept returning true!"); return true; } if (mIsUnableToDrag) { if (DEBUG) Log.v(TAG, "Intercept returning false!"); return false; } } switch (action) { case MotionEvent.ACTION_MOVE: { /* * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check * whether the user has moved far enough from his original down touch. */ /* * Locally do absolute value. mLastMotionY is set to the y value * of the down event. */ final int activePointerId = mActivePointerId; if (activePointerId == INVALID_POINTER) { // If we don't have a valid id, the touch down wasn't on content. break; } final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId); final float x = MotionEventCompat.getX(ev, pointerIndex); final float dx = x - mLastMotionX; final float xDiff = Math.abs(dx); final float y = MotionEventCompat.getY(ev, pointerIndex); final float yDiff = Math.abs(y - mLastMotionY); final int scrollX = getScrollX(); final boolean atEdge = (dx > 0 && scrollX == 0) || (dx < 0 && mAdapter != null && scrollX >= (mAdapter.getCount() - 1) * getWidth() - 1); if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff); if (canScroll(this, false, (int) dx, (int) x, (int) y)) { // Nested view has scrollable area under this point. Let it be handled there. mInitialMotionX = mLastMotionX = x; mLastMotionY = y; return false; } if (xDiff > mTouchSlop && xDiff > yDiff) { if (DEBUG) Log.v(TAG, "Starting drag!"); mIsBeingDragged = true; setScrollState(SCROLL_STATE_DRAGGING); mLastMotionX = x; setScrollingCacheEnabled(true); } else { if (yDiff > mTouchSlop) { // The finger has moved enough in the vertical // direction to be counted as a drag... abort // any attempt to drag horizontally, to work correctly // with children that have scrolling containers. if (DEBUG) Log.v(TAG, "Starting unable to drag!"); mIsUnableToDrag = true; } } break; } case MotionEvent.ACTION_DOWN: { /* * Remember location of down touch. * ACTION_DOWN always refers to pointer index 0. */ mLastMotionX = mInitialMotionX = ev.getX(); mLastMotionY = ev.getY(); mActivePointerId = MotionEventCompat.getPointerId(ev, 0); if (mScrollState == SCROLL_STATE_SETTLING) { // Let the user 'catch' the pager as it animates. mIsBeingDragged = true; mIsUnableToDrag = false; setScrollState(SCROLL_STATE_DRAGGING); } else { completeScroll(); mIsBeingDragged = false; mIsUnableToDrag = false; } if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY + " mIsBeingDragged=" + mIsBeingDragged + "mIsUnableToDrag=" + mIsUnableToDrag); break; } case MotionEventCompat.ACTION_POINTER_UP: onSecondaryPointerUp(ev); break; } /* * The only time we want to intercept motion events is if we are in the * drag mode. */ return false; } @Override public boolean onTouchEvent(MotionEvent ev) { if (mFakeDragging) { // A fake drag is in progress already, ignore this real one // but still eat the touch events. // (It is likely that the user is multi-touching the screen.) return true; } if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) { // Don't handle edge touches immediately -- they may actually belong to one of our // descendants. return false; } if (mAdapter == null || mAdapter.getCount() == 0) { // Nothing to present or scroll; nothing to touch. return false; } if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); final int action = ev.getAction(); boolean needsInvalidate = false; switch (action & MotionEventCompat.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { /* * If being flinged and user touches, stop the fling. isFinished * will be false if being flinged. */ completeScroll(); // Remember where the motion event started mLastMotionX = mInitialMotionX = ev.getX(); mActivePointerId = MotionEventCompat.getPointerId(ev, 0); break; } case MotionEvent.ACTION_MOVE: if (!mIsBeingDragged) { final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, pointerIndex); final float xDiff = Math.abs(x - mLastMotionX); final float y = MotionEventCompat.getY(ev, pointerIndex); final float yDiff = Math.abs(y - mLastMotionY); if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff); if (xDiff > mTouchSlop && xDiff > yDiff) { if (DEBUG) Log.v(TAG, "Starting drag!"); mIsBeingDragged = true; mLastMotionX = x; setScrollState(SCROLL_STATE_DRAGGING); setScrollingCacheEnabled(true); } } if (mIsBeingDragged) { // Scroll to follow the motion event final int activePointerIndex = MotionEventCompat.findPointerIndex( ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, activePointerIndex); final float deltaX = mLastMotionX - x; mLastMotionX = x; float oldScrollX = getScrollX(); float scrollX = oldScrollX + deltaX; final int width = getWidth(); final int widthWithMargin = width + mPageMargin; final int lastItemIndex = mAdapter.getCount() - 1; final float leftBound = Math.max(0, (mCurItem - 1) * widthWithMargin); final float rightBound = Math.min(mCurItem + 1, lastItemIndex) * widthWithMargin; if (scrollX < leftBound) { if (leftBound == 0) { float over = -scrollX; needsInvalidate = mLeftEdge.onPull(over / width); } scrollX = leftBound; } else if (scrollX > rightBound) { if (rightBound == lastItemIndex * widthWithMargin) { float over = scrollX - rightBound; needsInvalidate = mRightEdge.onPull(over / width); } scrollX = rightBound; } // Don't lose the rounded component mLastMotionX += scrollX - (int) scrollX; scrollTo((int) scrollX, getScrollY()); if (mOnPageChangeListener != null) { final int position = (int) scrollX / widthWithMargin; final int positionOffsetPixels = (int) scrollX % widthWithMargin; final float positionOffset = (float) positionOffsetPixels / widthWithMargin; mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } } break; case MotionEvent.ACTION_UP: if (mIsBeingDragged) { final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) VelocityTrackerCompat.getXVelocity( velocityTracker, mActivePointerId); mPopulatePending = true; final int widthWithMargin = getWidth() + mPageMargin; final int scrollX = getScrollX(); final int currentPage = scrollX / widthWithMargin; int nextPage = initialVelocity > 0 ? currentPage : currentPage + 1; setCurrentItemInternal(nextPage, true, true, initialVelocity); mActivePointerId = INVALID_POINTER; endDrag(); needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease(); } break; case MotionEvent.ACTION_CANCEL: if (mIsBeingDragged) { setCurrentItemInternal(mCurItem, true, true); mActivePointerId = INVALID_POINTER; endDrag(); needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease(); } break; case MotionEventCompat.ACTION_POINTER_DOWN: { final int index = MotionEventCompat.getActionIndex(ev); final float x = MotionEventCompat.getX(ev, index); mLastMotionX = x; mActivePointerId = MotionEventCompat.getPointerId(ev, index); break; } case MotionEventCompat.ACTION_POINTER_UP: onSecondaryPointerUp(ev); mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId)); break; } if (needsInvalidate) { invalidate(); } return false; } @Override public void draw(Canvas canvas) { super.draw(canvas); boolean needsInvalidate = false; final int overScrollMode = ViewCompat.getOverScrollMode(this); if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS || (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && mAdapter != null && mAdapter.getCount() > 1)) { if (!mLeftEdge.isFinished()) { final int restoreCount = canvas.save(); final int height = getHeight() - getPaddingTop() - getPaddingBottom(); canvas.rotate(270); canvas.translate(-height + getPaddingTop(), 0); mLeftEdge.setSize(height, getWidth()); needsInvalidate |= mLeftEdge.draw(canvas); canvas.restoreToCount(restoreCount); } if (!mRightEdge.isFinished()) { final int restoreCount = canvas.save(); final int width = getWidth(); final int height = getHeight() - getPaddingTop() - getPaddingBottom(); final int itemCount = mAdapter != null ? mAdapter.getCount() : 1; canvas.rotate(90); canvas.translate(-getPaddingTop(), -itemCount * (width + mPageMargin) + mPageMargin); mRightEdge.setSize(height, width); needsInvalidate |= mRightEdge.draw(canvas); canvas.restoreToCount(restoreCount); } } else { mLeftEdge.finish(); mRightEdge.finish(); } if (needsInvalidate) { // Keep animating invalidate(); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // Draw the margin drawable if needed. if (mPageMargin > 0 && mMarginDrawable != null) { final int scrollX = getScrollX(); final int width = getWidth(); final int offset = scrollX % (width + mPageMargin); if (offset != 0) { // Pages fit completely when settled; we only need to draw when in between final int left = scrollX - offset + width; mMarginDrawable.setBounds(left, 0, left + mPageMargin, getHeight()); mMarginDrawable.draw(canvas); } } } /** * Start a fake drag of the pager. * <p> * <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager * with the touch scrolling of another view, while still letting the ViewPager * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.) * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call * {@link #endFakeDrag()} to complete the fake drag and fling as necessary. * <p> * <p>During a fake drag the ViewPager will ignore all touch events. If a real drag * is already in progress, this method will return false. * * @return true if the fake drag began successfully, false if it could not be started. * @see #fakeDragBy(float) * @see #endFakeDrag() */ public boolean beginFakeDrag() { if (mIsBeingDragged) { return false; } mFakeDragging = true; setScrollState(SCROLL_STATE_DRAGGING); mInitialMotionX = mLastMotionX = 0; if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } else { mVelocityTracker.clear(); } final long time = SystemClock.uptimeMillis(); final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0); mVelocityTracker.addMovement(ev); ev.recycle(); mFakeDragBeginTime = time; return true; } /** * End a fake drag of the pager. * * @see #beginFakeDrag() * @see #fakeDragBy(float) */ public void endFakeDrag() { if (!mFakeDragging) { throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first."); } final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) VelocityTrackerCompat.getYVelocity( velocityTracker, mActivePointerId); mPopulatePending = true; if ((Math.abs(initialVelocity) > mMinimumVelocity) || Math.abs(mInitialMotionX - mLastMotionX) >= (getWidth() / 3)) { if (mLastMotionX > mInitialMotionX) { setCurrentItemInternal(mCurItem - 1, true, true); } else { setCurrentItemInternal(mCurItem + 1, true, true); } } else { setCurrentItemInternal(mCurItem, true, true); } endDrag(); mFakeDragging = false; } /** * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first. * * @param xOffset Offset in pixels to drag by. * @see #beginFakeDrag() * @see #endFakeDrag() */ public void fakeDragBy(float xOffset) { if (!mFakeDragging) { throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first."); } mLastMotionX += xOffset; float scrollX = getScrollX() - xOffset; final int width = getWidth(); final int widthWithMargin = width + mPageMargin; final float leftBound = Math.max(0, (mCurItem - 1) * widthWithMargin); final float rightBound = Math.min(mCurItem + 1, mAdapter.getCount() - 1) * widthWithMargin; if (scrollX < leftBound) { scrollX = leftBound; } else if (scrollX > rightBound) { scrollX = rightBound; } // Don't lose the rounded component mLastMotionX += scrollX - (int) scrollX; scrollTo((int) scrollX, getScrollY()); if (mOnPageChangeListener != null) { final int position = (int) scrollX / widthWithMargin; final int positionOffsetPixels = (int) scrollX % widthWithMargin; final float positionOffset = (float) positionOffsetPixels / widthWithMargin; mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } // Synthesize an event for the VelocityTracker. final long time = SystemClock.uptimeMillis(); final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE, mLastMotionX, 0, 0); mVelocityTracker.addMovement(ev); ev.recycle(); } /** * Returns true if a fake drag is in progress. * * @return true if currently in a fake drag, false otherwise. * @see #beginFakeDrag() * @see #fakeDragBy(float) * @see #endFakeDrag() */ public boolean isFakeDragging() { return mFakeDragging; } private void onSecondaryPointerUp(MotionEvent ev) { final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { // This was our active pointer going up. Choose a new // active pointer and adjust accordingly. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex); mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); if (mVelocityTracker != null) { mVelocityTracker.clear(); } } } private void endDrag() { mIsBeingDragged = false; mIsUnableToDrag = false; if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } private void setScrollingCacheEnabled(boolean enabled) { if (mScrollingCacheEnabled != enabled) { mScrollingCacheEnabled = enabled; if (USE_CACHE) { final int size = getChildCount(); for (int i = 0; i < size; ++i) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { child.setDrawingCacheEnabled(enabled); } } } } } /** * Tests scrollability within child views of v given a delta of dx. * * @param v View to test for horizontal scrollability * @param checkV Whether the view v passed should itself be checked for scrollability (true), * or just its children (false). * @param dx Delta scrolled in pixels * @param x X coordinate of the active touch point * @param y Y coordinate of the active touch point * @return true if child views of v can be scrolled by delta of dx. */ protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { if (v instanceof ViewGroup) { final ViewGroup group = (ViewGroup) v; final int scrollX = v.getScrollX(); final int scrollY = v.getScrollY(); final int count = group.getChildCount(); // Count backwards - let topmost views consume scroll distance first. for (int i = count - 1; i >= 0; i--) { // TODO: Add versioned support here for transformed views. // This will not work for transformed views in Honeycomb+ final View child = group.getChildAt(i); if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() && y + scrollY >= child.getTop() && y + scrollY < child.getBottom() && canScroll(child, true, dx, x + scrollX - child.getLeft(), y + scrollY - child.getTop())) { return true; } } } return checkV && ViewCompat.canScrollHorizontally(v, -dx); } @Override public boolean dispatchKeyEvent(KeyEvent event) { // Let the focused view and/or our descendants get the key first return super.dispatchKeyEvent(event) || executeKeyEvent(event); } /** * You can call this function yourself to have the scroll view perform * scrolling from a key event, just as if the event had been dispatched to * it by the view hierarchy. * * @param event The key event to execute. * @return Return true if the event was handled, else false. */ public boolean executeKeyEvent(KeyEvent event) { boolean handled = false; if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_LEFT: handled = arrowScroll(FOCUS_LEFT); break; case KeyEvent.KEYCODE_DPAD_RIGHT: handled = arrowScroll(FOCUS_RIGHT); break; case KeyEvent.KEYCODE_TAB: if (KeyEventCompat.hasNoModifiers(event)) { handled = arrowScroll(FOCUS_FORWARD); } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) { handled = arrowScroll(FOCUS_BACKWARD); } break; } } return handled; } public boolean arrowScroll(int direction) { View currentFocused = findFocus(); if (currentFocused == this) currentFocused = null; boolean handled = false; View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); if (nextFocused != null && nextFocused != currentFocused) { if (direction == View.FOCUS_LEFT) { // If there is nothing to the left, or this is causing us to // jump to the right, then what we really want to do is page left. if (currentFocused != null && nextFocused.getLeft() >= currentFocused.getLeft()) { handled = pageLeft(); } else { handled = nextFocused.requestFocus(); } } else if (direction == View.FOCUS_RIGHT) { // If there is nothing to the right, or this is causing us to // jump to the left, then what we really want to do is page right. if (currentFocused != null && nextFocused.getLeft() <= currentFocused.getLeft()) { handled = pageRight(); } else { handled = nextFocused.requestFocus(); } } } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) { // Trying to move left and nothing there; try to page. handled = pageLeft(); } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) { // Trying to move right and nothing there; try to page. handled = pageRight(); } if (handled) { playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); } return handled; } boolean pageLeft() { if (mCurItem > 0) { setCurrentItem(mCurItem - 1, true); return true; } return false; } boolean pageRight() { if (mAdapter != null && mCurItem < (mAdapter.getCount() - 1)) { setCurrentItem(mCurItem + 1, true); return true; } return false; } /** * We only want the current page that is being shown to be focusable. */ @Override public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { final int focusableCount = views.size(); final int descendantFocusability = getDescendantFocusability(); if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { for (int i = 0; i < getChildCount(); i++) { final View child = getChildAt(i); if (child.getVisibility() == VISIBLE) { ItemInfo ii = infoForChild(child); if (ii != null && ii.position == mCurItem) { child.addFocusables(views, direction, focusableMode); } } } } // we add ourselves (if focusable) in all cases except for when we are // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is // to avoid the focus search finding layouts when a more precise search // among the focusable children would be more interesting. if ( descendantFocusability != FOCUS_AFTER_DESCENDANTS || // No focusable descendants (focusableCount == views.size())) { // Note that we can't call the superclass here, because it will // add all views in. So we need to do the same thing View does. if (!isFocusable()) { return; } if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE && isInTouchMode() && !isFocusableInTouchMode()) { return; } if (views != null) { views.add(this); } } } /** * We only want the current page that is being shown to be touchable. */ @Override public void addTouchables(ArrayList<View> views) { // Note that we don't call super.addTouchables(), which means that // we don't call View.addTouchables(). This is okay because a ViewPager // is itself not touchable. for (int i = 0; i < getChildCount(); i++) { final View child = getChildAt(i); if (child.getVisibility() == VISIBLE) { ItemInfo ii = infoForChild(child); if (ii != null && ii.position == mCurItem) { child.addTouchables(views); } } } } /** * We only want the current page that is being shown to be focusable. */ @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { int index; int increment; int end; int count = getChildCount(); if ((direction & FOCUS_FORWARD) != 0) { index = 0; increment = 1; end = count; } else { index = count - 1; increment = -1; end = -1; } for (int i = index; i != end; i += increment) { View child = getChildAt(i); if (child.getVisibility() == VISIBLE) { ItemInfo ii = infoForChild(child); if (ii != null && ii.position == mCurItem) { if (child.requestFocus(direction, previouslyFocusedRect)) { return true; } } } } return false; } @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { // ViewPagers should only report accessibility info for the current page, // otherwise things get very confusing. // TODO: Should this note something about the paging container? final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); if (child.getVisibility() == VISIBLE) { final ItemInfo ii = infoForChild(child); if (ii != null && ii.position == mCurItem && child.dispatchPopulateAccessibilityEvent(event)) { return true; } } } return false; } private class PagerObserver extends DataSetObserver { @Override public void onChanged() { dataSetChanged(); } @Override public void onInvalidated() { dataSetChanged(); } }}
ShowingPager
package history.six.com.rushingdemo.view;import android.content.Context;import android.graphics.drawable.AnimationDrawable;import android.view.LayoutInflater;import android.view.View;import android.widget.FrameLayout;import android.widget.ImageView;import android.widget.TextView;import com.zhy.autolayout.AutoLinearLayout;import history.six.com.rushingdemo.R;import history.six.com.rushingdemo.application.MyApplication;import history.six.com.rushingdemo.interfaces.IResetShowingPageListener;import history.six.com.rushingdemo.utils.CommonUtils;import history.six.com.rushingdemo.utils.NetUtils;public abstract class ShowingPager extends FrameLayout implements View.OnClickListener { /** * 定义状态 */ public static final int STATE_LOADING = 1; public static final int STATE_LOAD_ERROR = 2; public static final int STATE_LOAD_SUCCESS = 3; public static final int STATE_NOLOGIN = 4; private final Context context; //定义当前状态 public int currentState = STATE_LOADING;//得到当前的状态 private final View view; private final View showLoadError, showLoading; private final FrameLayout showFrameLayout; private final AutoLinearLayout titleLayout; private final TextView showing_error_tv_reset; private IResetShowingPageListener iResetShowingPageListener; private final LayoutParams params; private final View titleView; private final ImageView drawableImage; public ShowingPager(Context context) { super(context); this.context = context; params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); //获取主布局视图 view = View.inflate(context, R.layout.showing_pager, null); titleLayout = (AutoLinearLayout) view.findViewById(R.id.showLinearLayoutTitle); showLoadError = view.findViewById(R.id.showLoadError); showLoading = view.findViewById(R.id.showLoading); drawableImage = (ImageView) showLoading.findViewById(R.id.drawableImage); drawableImage.setImageResource(R.drawable.loading_animation); AnimationDrawable animationDrawable = (AnimationDrawable) drawableImage.getDrawable(); animationDrawable.start(); showFrameLayout = (FrameLayout) view.findViewById(R.id.showFrameLayout); this.addView(view, params); //查找重置按钮 showing_error_tv_reset = (TextView) showLoadError.findViewById(R.id.showing_error_tv_reset); showing_error_tv_reset.setOnClickListener(this); //添加title布局 titleView = LayoutInflater.from(getContext()).inflate(R.layout.common_title, null); titleLayout.addView(titleView, params); setTitleView(titleView); /** * 添加成功视图 */ View successView = setSuccessView(); if (successView == null) { showFrameLayout.setVisibility(View.GONE); } else { showFrameLayout.removeAllViews(); showFrameLayout.addView(successView, params); } //设置是否需要Title titleLayout.setVisibility(needTitleView() ? VISIBLE : GONE); showPage(); } //添加成功的视图 public abstract View setSuccessView(); //添加Title public abstract void setTitleView(View titleView); //添加Title public abstract boolean needTitleView(); //设置当前状态 public void setCurrentState(StateType stateType) { currentState = stateType.currentState; showPage(); } private void showPage() { //在主线程执行 CommonUtils.runOnUIThread(new Runnable() { @Override public void run() { showPageOnUI(); } }); } private void showPageOnUI() { showLoading.setVisibility(currentState == STATE_LOADING ? View.VISIBLE : View.GONE); showLoadError.setVisibility(currentState == STATE_LOAD_ERROR ? View.VISIBLE : View.GONE); showFrameLayout.setVisibility(currentState == STATE_LOAD_SUCCESS ? View.VISIBLE : View.GONE); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.showing_error_tv_reset: this.setCurrentState(StateType.STATE_LOADING); if (NetUtils.isHaveNet()) { /*if (currentState != STATE_LOADING) currentState = STATE_LOADING; showPage(); onLoad();*/ if (iResetShowingPageListener != null) { iResetShowingPageListener.onResetClick(view); } } else { MyApplication.getHandler().postDelayed(new Runnable() { @Override public void run() { ShowingPager.this.setCurrentState(StateType.STATE_LOAD_ERROR); } }, 2000); } break; } } /** * 枚举类 */ public enum StateType { //请求类型 STATE_LOADING(1), STATE_LOAD_ERROR(2), STATE_LOAD_SUCCESS(3), STATE_NOLOGIN(4); private final int currentState; StateType(int currentState) { this.currentState = currentState; } public int getCurrentState() { return currentState; } } public void setIResetShowingPageListener(IResetShowingPageListener iResetShowingPageListener) { this.iResetShowingPageListener = iResetShowingPageListener; }}
0 0
- 项目开发框架搭建二
- QQ项目二之框架搭建
- Atlassian JIRA 插件开发(二) — 插件项目框架搭建
- ArcGIS Desktop开发框架快速搭建【二】
- Android APP 开发项目框架搭建
- 简单搭建iOS开发项目框架
- Android开发的项目框架的搭建
- openjpa框架入门_项目框架搭建(二)
- S2SH开发环境搭建、框架整合、项目基础环境搭建
- 搭建高质量的Android项目框架系列二
- 一起写RPC框架(二)RPC之项目搭建
- 利用Maven搭建Spring+SpringMVC+Mybatis框架项目(二)
- 【java】SSM框架搭建(二)——项目整合
- Linq项目开发全纪录之(二)项目搭建
- Spring2.5、Struts2、Ibatis开发框架搭建之二
- Spring2.5、Struts2、Ibatis开发框架搭建之二
- Extjs4开发笔记(二)——框架的搭建
- Felix+Struts2搭建OSGI Web开发框架(二)
- 10046 事件跟踪
- 求两个数中的较大者
- HDU1025 Constructing Roads In JGShining's Kingdom (LIS)
- Android 解析XML
- python配置一
- 项目开发框架搭建二
- 安装MYSQL-python
- 一张图说明,到底调谁的operator new
- python学习之列表
- 求三个数中最大者
- ros gdb
- OpenGL入门学习笔记(一)
- java 蓝桥杯 分解质因子
- 11G RAC OLR维护命令