【0089】【项目实战】-【谷歌电子市场】-【01】【项目介绍】【自定义Application】【UIUtils封装】【导入PagerTab并解决相关报错】【主页面框架搭建】【LoadingPage-加载中布局】【自定义进度条】【LoadingPage-加载失败布局】【数据为空的布局的加载】【LoadingPage-根据状态显示页面】【onCreateSuccessView实现】

1.项目介绍

【服务器】在手机上安装了一个服务器;

【首页】

【下载的页面】具有断点续传的功能;

【推荐】随机显示的数据;

【分类的页面】

 

【排行的页面】颜色宽高都是随机的;

【侧边栏】

2.如何运行项目

2.1 目录介绍

2.2 项目的编码

【说明】使用utf-8编码;

2.3 WebServer启动和资源

【说明】需要在手机客户端启动WebServer的应用程序;

              资源文件是放在sd卡中的;

              需要将资源文件放到sd卡的根目录下;

【image目录】项目中使用的图片等

【app目录】

 

3. 自定义Application

【说明】定义一些常用的全局的变量;

4.UIUtils封装

【说明】整个项目全局的获取一些常用的信息,Application类定义的常用的全局的类 都可以从此类中获取到;

 

【获取字符串】

【获取字符串数组】

 

【获取图片-颜色】

【获取尺寸】

【dp和px之间的转换】

【加载布局文件】

 

【判断是否运行在主线程】

【运行在中主线程】

【完整源码】/GooglePlay74/src/com/itheima/googleplay74/utils/UIUtils.java

 1 package com.itheima.googleplay74.utils;
 2 
 3 import android.content.Context;
 4 import android.content.res.ColorStateList;
 5 import android.graphics.drawable.Drawable;
 6 import android.os.Handler;
 7 import android.view.View;
 8 
 9 import com.itheima.googleplay74.global.GooglePlayApplication;
10 
11 public class UIUtils {
12 
13     public static Context getContext() {
14         return GooglePlayApplication.getContext();
15     }
16 
17     public static Handler getHandler() {
18         return GooglePlayApplication.getHandler();
19     }
20 
21     public static int getMainThreadId() {
22         return GooglePlayApplication.getMainThreadId();
23     }
24 
25     // /////////////////加载资源文件 ///////////////////////////
26 
27     // 获取字符串
28     public static String getString(int id) {
29         return getContext().getResources().getString(id);
30     }
31 
32     // 获取字符串数组
33     public static String[] getStringArray(int id) {
34         return getContext().getResources().getStringArray(id);
35     }
36 
37     // 获取图片
38     public static Drawable getDrawable(int id) {
39         return getContext().getResources().getDrawable(id);
40     }
41 
42     // 获取颜色
43     public static int getColor(int id) {
44         return getContext().getResources().getColor(id);
45     }
46     
47     //根据id获取颜色的状态选择器
48     public static ColorStateList getColorStateList(int id) {
49         return getContext().getResources().getColorStateList(id);
50     }
51 
52     // 获取尺寸
53     public static int getDimen(int id) {
54         return getContext().getResources().getDimensionPixelSize(id);// 返回具体像素值
55     }
56 
57     // /////////////////dip和px转换//////////////////////////
58 
59     public static int dip2px(float dip) {
60         float density = getContext().getResources().getDisplayMetrics().density;
61         return (int) (dip * density + 0.5f);
62     }
63 
64     public static float px2dip(int px) {
65         float density = getContext().getResources().getDisplayMetrics().density;
66         return px / density;
67     }
68 
69     // /////////////////加载布局文件//////////////////////////
70     public static View inflate(int id) {
71         return View.inflate(getContext(), id, null);
72     }
73 
74     // /////////////////判断是否运行在主线程//////////////////////////
75     public static boolean isRunOnUIThread() {
76         // 获取当前线程id, 如果当前线程id和主线程id相同, 那么当前就是主线程
77         int myTid = android.os.Process.myTid();
78         if (myTid == getMainThreadId()) {
79             return true;
80         }
81 
82         return false;
83     }
84 
85     // 运行在主线程
86     public static void runOnUIThread(Runnable r) {
87         if (isRunOnUIThread()) {
88             // 已经是主线程, 直接运行
89             r.run();
90         } else {
91             // 如果是子线程, 借助handler让其运行在主线程
92             getHandler().post(r);
93         }
94     }
95 
96 }

5.导入PagerTab并解决相关报错

 5.1【说明】在主页当中搭建指示器

5.2 小技巧

【说明】将一个项目中的文件移动的时候一定要使用refactor->move;

 这样相关类的关系都可以移动修改。

5.3 将PagerTab.java源码导入及错误修改

【修改的错误】

【PagerTab.java源码】未修改的文件

  1 package com.mwqi.ui.widget;
  2 
  3 import android.content.Context;
  4 import android.graphics.Canvas;
  5 import android.graphics.Paint;
  6 import android.graphics.Typeface;
  7 import android.support.v4.view.ViewCompat;
  8 import android.support.v4.view.ViewPager;
  9 import android.support.v4.view.ViewPager.OnPageChangeListener;
 10 import android.support.v4.widget.EdgeEffectCompat;
 11 import android.support.v4.widget.ScrollerCompat;
 12 import android.util.AttributeSet;
 13 import android.util.TypedValue;
 14 import android.view.*;
 15 import android.widget.ImageButton;
 16 import android.widget.TextView;
 17 import com.mwqi.R;
 18 import com.mwqi.ui.activity.BaseActivity;
 19 import com.mwqi.utils.UIUtils;
 20 
 21 public class PagerTab extends ViewGroup {
 22 
 23     private ViewPager mViewPager;
 24     private PageListener mPageListener = new PageListener();//用于注册给ViewPager监听状态和滚动
 25     private OnPageChangeListener mDelegatePageListener;//用于通知外界ViewPager的状态和滚动
 26     private BaseActivity mActivity;
 27 
 28     private int mDividerPadding = 12;// 分割线上下的padding
 29     private int mDividerWidth = 1;// 分割线的宽度
 30     private int mDividerColor = 0x1A000000;//分割线颜色
 31     private Paint mDividerPaint;//分割线的画笔
 32 
 33     private int mIndicatorHeight = 4;//指示器的高度
 34     private int mIndicatorWidth;//指示器的宽度,是动态的随着tab的宽度变化
 35     private int mIndicatorLeft;//指示器的距离左边的距离
 36     private int mIndicatorColor = 0xFF0084FF;//指示器颜色
 37     private Paint mIndicatorPaint; //指示器的画笔
 38 
 39     private int mContentWidth;//记录自身内容的宽度
 40     private int mContentHeight;//记录自身内容的高度
 41 
 42     private int mTabPadding = 24;// tab左右的内边距
 43     private int mTabTextSize = 16; //tab文字大小
 44     private int mTabBackgroundResId = R.drawable.bg_tab_text;// tab背景资源
 45     private int mTabTextColorResId = R.color.tab_text_color; //tab文字颜色
 46     private int mTabCount;//tab的个数
 47 
 48     private int mCurrentPosition = 0;//当前光标所处的tab,规则是以光标的最左端所在的item的position
 49     private float mCurrentOffsetPixels;//光标左边距离当前光标所处的tab的左边距离
 50     private int mSelectedPosition = 0; //当前被选中的tab,用于记录手指点击tab的position
 51 
 52     private boolean mIsBeingDragged = false;//是否处于拖动中
 53     private float mLastMotionX;//上一次手指触摸的x坐标
 54     private VelocityTracker mVelocityTracker;//用于记录速度的帮助类
 55     private int mMinimumVelocity;//系统默认的最小满足fling的速度
 56     private int mMaximumVelocity;//系统默认最大的fling速度
 57     private int mTouchSlop;//系统默认满足滑动的最小位移
 58 
 59     private ScrollerCompat mScroller;//处理滚动的帮助者
 60     private int mLastScrollX;//记录上一次滚动的x位置,这是用于处理overScroll,实际位置可能会受到限制
 61 
 62     private int mMaxScrollX = 0;// 控件最大可滚动的距离
 63     private int mSplitScrollX = 0;// 根据item的个数,计算出每移动一个item控件需要移动的距离
 64 
 65     private EdgeEffectCompat mLeftEdge;//处理overScroll的反馈效果
 66     private EdgeEffectCompat mRightEdge;
 67 
 68     public PagerTab(Context context) {
 69         this(context, null);
 70     }
 71 
 72     public PagerTab(Context context, AttributeSet attrs) {
 73         this(context, attrs, 0);
 74     }
 75 
 76     public PagerTab(Context context, AttributeSet attrs, int defStyle) {
 77         super(context, attrs, defStyle);
 78         if (context instanceof BaseActivity) {
 79             mActivity = (BaseActivity) context;
 80         }
 81         init();
 82         initPaint();
 83     }
 84 
 85     /** 初始化一些常量 */
 86     private void init() {
 87         //把一个值从dip转换成px
 88         mIndicatorHeight = UIUtils.dip2px(mIndicatorHeight);
 89         mDividerPadding = UIUtils.dip2px(mDividerPadding);
 90         mTabPadding = UIUtils.dip2px(mTabPadding);
 91         mDividerWidth = UIUtils.dip2px(mDividerWidth);
 92         mTabTextSize = UIUtils.dip2px(mTabTextSize);
 93         //创建一个scroller
 94         mScroller = ScrollerCompat.create(mActivity);
 95         //获取一个系统关于View的常量配置类
 96         final ViewConfiguration configuration = ViewConfiguration.get(mActivity);
 97         //获取滑动的最小距离
 98         mTouchSlop = configuration.getScaledTouchSlop();
 99         //获取fling的最小速度
100         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
101         //获取fling的最大速度
102         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
103 
104         mLeftEdge = new EdgeEffectCompat(mActivity);
105         mRightEdge = new EdgeEffectCompat(mActivity);
106     }
107 
108     /** 初始化笔 */
109     private void initPaint() {
110         mIndicatorPaint = new Paint();
111         mIndicatorPaint.setAntiAlias(true);
112         mIndicatorPaint.setStyle(Paint.Style.FILL);
113         mIndicatorPaint.setColor(mIndicatorColor);
114 
115         mDividerPaint = new Paint();
116         mDividerPaint.setAntiAlias(true);
117         mDividerPaint.setStrokeWidth(mDividerWidth);
118         mDividerPaint.setColor(mDividerColor);
119     }
120 
121     /** 设置ViewPager */
122     public void setViewPager(ViewPager viewPager) {
123         if (viewPager == null || viewPager.getAdapter() == null) {
124             throw new IllegalStateException("ViewPager is null or ViewPager does not have adapter instance.");
125         }
126         mViewPager = viewPager;
127         onViewPagerChanged();
128     }
129 
130     private void onViewPagerChanged() {
131         mViewPager.setOnPageChangeListener(mPageListener);//给ViewPager设置监听
132         mTabCount = mViewPager.getAdapter().getCount();//有多少个tab需要看ViewPager有多少个页面
133         for (int i = 0; i < mTabCount; i++) {
134             if (mViewPager.getAdapter() instanceof IconTabProvider) {//如果想要使用icon作为tab,则需要adapter实现IconTabProvider接口
135                 addIconTab(i, ((IconTabProvider) mViewPager.getAdapter()).getPageIconResId(i));
136             } else {
137                 addTextTab(i, mViewPager.getAdapter().getPageTitle(i).toString());
138             }
139         }
140         ViewTreeObserver viewTreeObserver = getViewTreeObserver();
141         if (viewTreeObserver != null) {//监听第一个的全局layout事件,来设置当前的mCurrentPosition,显示对应的tab
142             viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
143                 @Override
144                 public void onGlobalLayout() {
145                     getViewTreeObserver().removeGlobalOnLayoutListener(this);//只需要监听一次,之后通过listener回调即可
146                     mCurrentPosition = mViewPager.getCurrentItem();
147                     if (mDelegatePageListener != null) {
148                         mDelegatePageListener.onPageSelected(mCurrentPosition);
149                     }
150                 }
151             });
152         }
153     }
154 
155     /** 设置监听,因为Tab会监听ViewPager的状态,所以不要给ViewPager设置监听了,设置给Tab,由Tab转发 */
156     public void setOnPageChangeListener(OnPageChangeListener listener) {
157         mDelegatePageListener = listener;
158     }
159 
160     /** 添加文字tab */
161     private void addTextTab(final int position, String title) {
162         TextView tab = new TextView(mActivity);
163         tab.setText(title);
164         tab.setGravity(Gravity.CENTER);
165         tab.setSingleLine();
166         tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTabTextSize);
167         tab.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
168         tab.setTextColor(UIUtils.getColorStateList(mTabTextColorResId));
169         tab.setBackgroundDrawable(UIUtils.getDrawable(mTabBackgroundResId));
170         tab.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
171         addTab(position, tab);
172     }
173 
174     /** 添加图片icon */
175     private void addIconTab(final int position, int resId) {
176         ImageButton tab = new ImageButton(mActivity);
177         tab.setImageResource(resId);
178         tab.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
179         addTab(position, tab);
180     }
181 
182     private void addTab(final int position, View tab) {
183         tab.setFocusable(true);
184         //设置tab的点击事件,当tab被点击时候切换pager的页面
185         tab.setOnClickListener(new OnClickListener() {
186             @Override
187             public void onClick(View v) {
188                 mViewPager.setCurrentItem(position);
189             }
190         });
191         tab.setPadding(mTabPadding, 0, mTabPadding, 0);
192         addView(tab, position);
193     }
194 
195     /** 测量时的回调 */
196     @Override
197     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
198         // 获取控件自身的宽高,模式
199         int widthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
200         int heightSize = MeasureSpec.getSize(heightMeasureSpec) - getPaddingBottom() - getPaddingBottom();
201         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
202         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
203 
204         int totalWidth = 0;
205         int highest = 0;
206         int goneChildCount = 0;
207         for (int i = 0; i < mTabCount; i++) {
208             final View child = getChildAt(i);
209             if (child == null || child.getVisibility() == View.GONE) {
210                 goneChildCount--;
211                 continue;
212             }
213             int childWidthMeasureSpec;
214             int childHeightMeasureSpec;
215 
216             LayoutParams childLayoutParams = child.getLayoutParams();
217             if (childLayoutParams == null) {
218                 childLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
219             }
220 
221             if (childLayoutParams.width == LayoutParams.MATCH_PARENT) {
222                 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
223             } else if (childLayoutParams.width == LayoutParams.WRAP_CONTENT) {
224                 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
225             } else {
226                 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childLayoutParams.width, MeasureSpec.EXACTLY);
227             }
228 
229             if (childLayoutParams.height == LayoutParams.MATCH_PARENT) {
230                 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
231             } else if (childLayoutParams.height == LayoutParams.WRAP_CONTENT) {
232                 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.AT_MOST);
233             } else {
234                 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childLayoutParams.height, MeasureSpec.EXACTLY);
235             }
236 
237             child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
238 
239             int childWidth = child.getMeasuredWidth();
240             int childHeight = child.getMeasuredHeight();
241 
242             totalWidth += childWidth;
243             highest = highest < childHeight ? childHeight : highest;
244         }
245 
246         if (totalWidth <= widthSize) {//如果子Tab的总宽度小于PagerTab,则采用平分模式
247             int splitWidth = (int) (widthSize / (mTabCount - goneChildCount + 0.0f) + 0.5f);
248             for (int i = 0; i < mTabCount; i++) {
249                 final View child = getChildAt(i);
250                 if (child == null || child.getVisibility() == View.GONE) {
251                     continue;
252                 }
253                 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(splitWidth, MeasureSpec.EXACTLY);
254                 int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), MeasureSpec.EXACTLY);
255                 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
256             }
257             mMaxScrollX = 0;
258             mSplitScrollX = 0;
259         } else {//如果所有子View大于控件的宽度
260             mMaxScrollX = totalWidth - widthSize;
261             mSplitScrollX = (int) (mMaxScrollX / (mTabCount - goneChildCount - 1.0f) + 0.5f);
262         }
263 
264         if (widthMode == MeasureSpec.EXACTLY) {
265             mContentWidth = widthSize;
266         } else {
267             mContentWidth = totalWidth;
268         }
269 
270         if (heightMode == MeasureSpec.EXACTLY) {
271             mContentHeight = heightSize;
272         } else {
273             mContentHeight = highest;
274         }
275 
276         int measureWidth = mContentWidth + getPaddingLeft() + getPaddingRight();
277         int measureHeight = mContentHeight + getPaddingTop() + getPaddingBottom();
278         setMeasuredDimension(measureWidth, measureHeight);
279     }
280 
281     /** 布局时的回调 */
282     @Override
283     protected void onLayout(boolean changed, int l, int t, int r, int b) {//这里简化了,没有考虑margin的情况
284         if (changed) {
285             int height = b - t;//控件供子View显示的高度
286             int left = l;
287             for (int i = 0; i < mTabCount; i++) {
288                 final View child = getChildAt(i);
289                 if (child == null || child.getVisibility() == View.GONE) {
290                     continue;
291                 }
292                 int top = (int) ((height - child.getMeasuredHeight()) / 2.0f + 0.5f);//如果控件比tab要高,则居中显示
293                 int right = left + child.getMeasuredWidth();
294                 child.layout(left, top, right, top + child.getMeasuredHeight());//摆放tab
295                 left = right;//因为是水平摆放的,所以为下一个准备left值
296             }
297         }
298     }
299 
300     /** 绘制时的回调 */
301     @Override
302     protected void onDraw(Canvas canvas) {
303         super.onDraw(canvas);
304         final int height = getHeight();
305         //画指示器
306         canvas.drawRect(mIndicatorLeft, height - mIndicatorHeight, mIndicatorLeft + mIndicatorWidth, height, mIndicatorPaint);
307 
308         // 画分割线
309         for (int i = 0; i < mTabCount - 1; i++) {//分割线的个数比tab的个数少一个
310             final View child = getChildAt(i);
311             if (child == null || child.getVisibility() == View.GONE) {
312                 continue;
313             }
314             if (child != null) {
315                 canvas.drawLine(child.getRight(), mDividerPadding, child.getRight(), mContentHeight - mDividerPadding, mDividerPaint);
316             }
317         }
318         // 因为overScroll效果是一个持续效果,所以需要持续画
319         boolean needsInvalidate = false;
320         if (!mLeftEdge.isFinished()) {//如果效果没停止
321             final int restoreCount = canvas.save();//先保存当前画布
322             final int heightEdge = getHeight() - getPaddingTop() - getPaddingBottom();
323             final int widthEdge = getWidth();
324             canvas.rotate(270);
325             canvas.translate(-heightEdge + getPaddingTop(), 0);
326             mLeftEdge.setSize(heightEdge, widthEdge);
327             needsInvalidate |= mLeftEdge.draw(canvas);
328             canvas.restoreToCount(restoreCount);
329         }
330         if (!mRightEdge.isFinished()) {
331             final int restoreCount = canvas.save();
332             final int widthEdge = getWidth();
333             final int heightEdge = getHeight() - getPaddingTop() - getPaddingBottom();
334             canvas.rotate(90);
335             canvas.translate(-getPaddingTop(), -(widthEdge + mMaxScrollX));
336             mRightEdge.setSize(heightEdge, widthEdge);
337             needsInvalidate |= mRightEdge.draw(canvas);
338             canvas.restoreToCount(restoreCount);
339         }
340         if (needsInvalidate) {
341             postInvalidate();
342         }
343     }
344 
345     /** 触摸事件是否拦截的方法 */
346     @Override
347     public boolean onInterceptTouchEvent(MotionEvent ev) {
348         final int action = ev.getAction();
349         if (mIsBeingDragged && action == MotionEvent.ACTION_MOVE) {//当已经处于拖动,并且当前事件是MOVE,直接消费掉
350             return true;
351         }
352         switch (action) {
353             case MotionEvent.ACTION_DOWN: {
354                 final float x = ev.getX();
355                 mLastMotionX = x; //记录住当前的x坐标
356                 mIsBeingDragged = !mScroller.isFinished();//如果按下的时候还在滚动,则把状态处于拖动状态
357                 break;
358             }
359             case MotionEvent.ACTION_MOVE: {
360                 final float x = ev.getX();
361                 final int xDiff = (int) Math.abs(x - mLastMotionX);//计算两次的差值
362                 if (xDiff > mTouchSlop) {//如果大于最小移动的距离,则把状态改变为拖动状态
363                     mIsBeingDragged = true;
364                     mLastMotionX = x;
365                     ViewParent parent = getParent();//并请求父View不要再拦截自己触摸事件,交给自己处理
366                     if (parent != null) {
367                         parent.requestDisallowInterceptTouchEvent(true);
368                     }
369                 }
370                 break;
371             }
372             case MotionEvent.ACTION_CANCEL://当手指离开或者触摸事件取消的时候,把拖动状态取消掉
373             case MotionEvent.ACTION_UP:
374                 mIsBeingDragged = false;
375                 break;
376         }
377         return mIsBeingDragged;//如果是拖动状态,则拦截事件,交给自己的onTouch处理
378     }
379 
380     /** 触摸事件的处理方法 */
381     public boolean onTouchEvent(MotionEvent ev) {
382         if (mVelocityTracker == null) {
383             mVelocityTracker = VelocityTracker.obtain();
384         }
385         mVelocityTracker.addMovement(ev);
386         final int action = ev.getAction();
387         switch (action) {
388             case MotionEvent.ACTION_DOWN: {//如果是down事件,记录住当前的x坐标
389                 final float x = ev.getX();
390                 if (!mScroller.isFinished()) {
391                     mScroller.abortAnimation();
392                 }
393                 mLastMotionX = x;
394                 break;
395             }
396             case MotionEvent.ACTION_MOVE: {
397                 final float x = ev.getX();
398                 final float deltaX = x - mLastMotionX;
399                 if (!mIsBeingDragged) {//如果还没有处于拖动,则判断两次的差值是否大于最小拖动的距离
400                     if (Math.abs(deltaX) > mTouchSlop) {
401                         mIsBeingDragged = true;
402                     }
403                 }
404                 if (mIsBeingDragged) {//如果处于拖动状态,记录住x坐标
405                     mLastMotionX = x;
406                     onMove(deltaX);
407                 }
408                 break;
409             }
410             case MotionEvent.ACTION_UP: {
411                 if (mIsBeingDragged) {
412                     final VelocityTracker velocityTracker = mVelocityTracker;
413                     //先对速度进行一个调整,第一个参数是时间单位,1000毫秒,第二个参数是最大速度。
414                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
415                     float velocity = velocityTracker.getXVelocity();//获取水平方向上的速度
416                     onUp(velocity);
417                 }
418             }
419             case MotionEvent.ACTION_CANCEL: {
420                 mIsBeingDragged = false;
421                 if (mVelocityTracker != null) {
422                     mVelocityTracker.recycle();
423                     mVelocityTracker = null;
424                 }
425                 break;
426             }
427         }
428         return true;
429     }
430 
431     private void onMove(float x) {
432         if (mMaxScrollX <= 0) {
433             if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {
434                 mViewPager.fakeDragBy(x);
435             }
436         } else {
437             int scrollByX = -(int) (x + 0.5);
438             if (getScrollX() + scrollByX < 0) {
439                 scrollByX = 0 - getScrollX();
440                 mLeftEdge.onPull(Math.abs(x) / getWidth());
441             }
442             if (getScrollX() + scrollByX > mMaxScrollX) {
443                 scrollByX = mMaxScrollX - getScrollX();
444                 mRightEdge.onPull(Math.abs(x) / getWidth());
445             }
446             scrollBy(scrollByX, 0);
447             ViewCompat.postInvalidateOnAnimation(this);
448         }
449     }
450 
451     private void onUp(float velocity) {
452         if (mMaxScrollX <= 0) {
453             if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();
454         } else {
455             if (Math.abs(velocity) <= mMinimumVelocity) {
456                 return;
457             }
458             mScroller.fling(getScrollX(), 0, -(int) (velocity + 0.5), 0, 0, mMaxScrollX, 0, 0, 270, 0);
459             ViewCompat.postInvalidateOnAnimation(this);
460         }
461     }
462 
463     @Override
464     public void computeScroll() {
465         if (mScroller.computeScrollOffset()) {
466             int oldX = mLastScrollX;
467             mLastScrollX = mScroller.getCurrX();
468             if (mLastScrollX < 0 && oldX >= 0) {
469                 mLeftEdge.onAbsorb((int) mScroller.getCurrVelocity());
470             } else if (mLastScrollX > mMaxScrollX && oldX <= mMaxScrollX) {
471                 mRightEdge.onAbsorb((int) mScroller.getCurrVelocity());
472             }
473             int x = mLastScrollX;
474             if (mLastScrollX < 0) {
475                 x = 0;
476             } else if (mLastScrollX > mMaxScrollX) {
477                 x = mMaxScrollX;
478             }
479             scrollTo(x, 0);
480         }
481         ViewCompat.postInvalidateOnAnimation(this);
482     }
483 
484     /** 检测mIndicatorOffset的合法性,并计算出其他有关tab的属性值 */
485     private void checkAndcalculate() {
486         // 如果指示器起始位置比第一个tab的起始位置还要小,纠正为第一个tab的起始位置,指示器宽度就是第一个tab的宽度
487         final View firstTab = getChildAt(0);
488         if (mIndicatorLeft < firstTab.getLeft()) {
489             mIndicatorLeft = firstTab.getLeft();
490             mIndicatorWidth = firstTab.getWidth();
491         }
492         // 如果指示器起始位置比最后一个tab的起始位置还要大,纠正为最后一个tab的起始位置,指示器宽度就是最后一个tab的宽度
493         View lastTab = getChildAt(mTabCount - 1);
494         if (mIndicatorLeft > lastTab.getLeft()) {
495             mIndicatorLeft = lastTab.getLeft();
496             mIndicatorWidth = lastTab.getWidth();
497         }
498         // 通过指示器的起始位置计算出当前处于第几个position,并且计算出已经偏移了多少,偏移量是以当前所处的tab的宽度的百分比
499         for (int i = 0; i < mTabCount; i++) {
500             View tab = getChildAt(i);
501             if (mIndicatorLeft < tab.getLeft()) {
502                 mCurrentPosition = i - 1;
503                 View currentTab = getChildAt(mCurrentPosition);
504                 mCurrentOffsetPixels = (mIndicatorLeft - currentTab.getLeft()) / (currentTab.getWidth() + 0.0f);
505                 break;
506             }
507         }
508     }
509 
510     /** 滚动到指定的child */
511     public void scrollSelf(int position, float offset) {
512         if (position >= mTabCount) {
513             return;
514         }
515         final View tab = getChildAt(position);
516         mIndicatorLeft = (int) (tab.getLeft() + tab.getWidth() * offset + 0.5);
517         int rightPosition = position + 1;
518         if (offset > 0 && rightPosition < mTabCount) {
519             View rightTab = getChildAt(rightPosition);
520             mIndicatorWidth = (int) (tab.getWidth() * (1 - offset) + rightTab.getWidth() * offset + 0.5);
521         } else {
522             mIndicatorWidth = tab.getWidth();
523         }
524         checkAndcalculate();
525 
526         int newScrollX = position * mSplitScrollX + (int) (offset * mSplitScrollX + 0.5);
527         if (newScrollX < 0) {
528             newScrollX = 0;
529         }
530         if (newScrollX > mMaxScrollX) {
531             newScrollX = mMaxScrollX;
532         }
533         //scrollTo(newScrollX, 0);//滑动
534         int duration = 100;
535         if (mSelectedPosition != -1) {
536             duration = (Math.abs(mSelectedPosition - position)) * 100;
537         }
538         mScroller.startScroll(getScrollX(), 0, (newScrollX - getScrollX()), 0, duration);
539         ViewCompat.postInvalidateOnAnimation(this);
540     }
541 
542     /** 选中指定位置的Tab */
543     private void selectTab(int position) {
544         for (int i = 0; i < mTabCount; i++) {
545             View tab = getChildAt(i);
546             if (tab != null) {
547                 tab.setSelected(position == i);
548             }
549         }
550     }
551 
552     /** ViewPager的OnPageChangeListener实现类,因为我们需要在PagerTab中获取PagerView的监听,以便可以调整tab */
553     private class PageListener implements OnPageChangeListener {
554         @Override
555         public void onPageScrolled(int position, float positionOffset, final int positionOffsetPixels) {
556             //根据VierPager的偏移值来滚动tab
557             scrollSelf(position, positionOffset);
558             if (mDelegatePageListener != null) {//这个是提供给外部的
559                 mDelegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
560             }
561         }
562 
563         @Override
564         public void onPageScrollStateChanged(int state) {
565             if (state == ViewPager.SCROLL_STATE_IDLE) {
566                 mSelectedPosition = -1;
567             }
568             if (mDelegatePageListener != null) {
569                 mDelegatePageListener.onPageScrollStateChanged(state);
570             }
571         }
572 
573         @Override
574         public void onPageSelected(int position) {
575             System.out.println("onPageSelected:" + position);
576             mSelectedPosition = position;
577             selectTab(position);
578             if (mDelegatePageListener != null) {
579                 mDelegatePageListener.onPageSelected(position);
580             }
581         }
582     }
583 
584     /** 如果指示器希望是图片,则继承该接口 */
585     public interface IconTabProvider {
586         public int getPageIconResId(int position);
587         public int getPageSelectedIconResId();
588     }
589 }

 【修改1】新建类BaseActivity.java继承与Activity;

[添加v7的兼容包]该包可以兼容2.x版本的android系统;其实在android4.x版本已经支持了actionBar;但是为了兼容其他2.x以上的android系统,需要增加此包;

【修改2】 状态选择器;

[新建背景图片选择器]

[新建文字颜色选择器]选中和按下的颜色都会改变;

【修改3】导包

【修改4】缺少此方法,增加此方法;

【使用该自定义的控件】使用自定义的TapPager控件必须设置其背景,否则会运行报错

 6.主页面框架搭建

【说明】MainActivity继承BaseActivity,可以拥有ActionBar的效果;

【增加id】

6.1 VIewPager中的页面的添加

【说明】在ViewPager中存在7个页面,每个页面都是一个fragment;

【fragment的数据适配器】该适配器其实是ViewPager的子类,重写的方法比较少;

【适配器数据的设置/指针和页面的绑定】绑定:指示器和下面的页面中的ViewPager的绑定;

【返回页签的标题】

6.2 生产fragment的工厂

【新建factory类】专门生产fragment的类;

【新建基类】新建一个fragment的基类,抽取共同的特性

【添加各自的fragment】完善factory类

 

【源码】思想:没有必要重复建立fragment,可以使用集合放入之后循环使用,提高性能;

 1 package com.itheima.googleplay74.ui.fragment;
 2 
 3 import java.util.HashMap;
 4 
 5 /**
 6  * 生产fragment工厂
 7  * 
 8  * @author Kevin
 9  * @date 2015-10-27
10  */
11 public class FragmentFactory {
12 
13     private static HashMap<Integer, BaseFragment> mFragmentMap = new HashMap<Integer, BaseFragment>();
14 
15     public static BaseFragment createFragment(int pos) {
16         // 先从集合中取, 如果没有,才创建对象, 提高性能
17         BaseFragment fragment = mFragmentMap.get(pos);
18 
19         if (fragment == null) {
20             switch (pos) {
21             case 0:
22                 fragment = new HomeFragment();
23                 break;
24             case 1:
25                 fragment = new AppFragment();
26                 break;
27             case 2:
28                 fragment = new GameFragment();
29                 break;
30             case 3:
31                 fragment = new SubjectFragment();
32                 break;
33             case 4:
34                 fragment = new RecommendFragment();
35                 break;
36             case 5:
37                 fragment = new CategoryFragment();
38                 break;
39             case 6:
40                 fragment = new HotFragment();
41                 break;
42 
43             default:
44                 break;
45             }
46 
47             mFragmentMap.put(pos, fragment);// 将fragment保存在集合中
48         }
49 
50         return fragment;
51     }
52 }

6.3 适配器中数据返回fragment

6.4 BUG

 【原因】

【解决方法】设置亮色的主题

【运行效果】临时测试写了下面的显示TextView;

7.LoadingPage-加载中布局

7.1 标签页中的共同的内容

【加载失败】页面都一致;

【加载中的状态】具有一个进度条,页面都一致;

【加载数据为空的页面】服务器没有数据的话,显示的页面是一致的;

7.2 在BaseActivity中构建共同的页面

【思路】共同的页面显示的内容就是下面蓝框显示的区域,可以自定义一个帧布局,根据不同的情况,显示不同的加载的页面;

【新建加载类】

【自定义加载的状态】

7.3【初始化加载中的布局】

【加载中的布局文件】

【初始化加载中的布局】

【加载中的布局的给标签页的设置分配】

【效果】因为在父类中设置了正在加载的布局,因此在其他的标签页面都会显示加载中的页面的效果;

8.自定义进度条

【效果】自定义进度条,黑白相间的转动

【原理】使用两张图片同时进行旋转,方向相反;

【代码书写】

【progressBar设置动画】

【效果】比较单调,效果一直是重复;

 【效果改进】同样时间,一方跑的比较快,跑的角度要大;

【效果】

 

9.LoadingPage-加载失败布局

【效果】

【布局】

【button的状态选择器】

 【帧布局中放入加载失败的布局】

10.数据为空的布局的加载

 

11.LoadingPage-根据状态显示页面

 

【效果】切换当前的状态值,可以分别查看显示的布局的样式;

12.onCreateSuccessView实现

【说明】加载成功之后的各个页面的内容不一致,需要写一个抽象的方法,让各个页面的子类实现该抽象方法,

进而加载不同的页面内容;

【说明】注意同名方法的调用,容易栈溢出;

【总结】实现的关系

13.LoadingPage-异步加载数据的封装

13.1  加载数据成功及加载数据返回状态

【初始化数据initData】

【改方法名initData改为onLoad】

【基类onLoad方法】

【子类实现基类的onLoad方法】

【总结】

【枚举的使用】相当于创建新的对象;

[枚举当中可以带有构造方法,带有参数]

【子类数据请求之后返回的值】开启子线程,加载数据,加载数据根据返回的状态,重新刷新显示页面;

【更新UI需要放到主线程】调用之前写好的UIUtils的方法,将加载数据更新UI切换到主线程中;

13.2  加载数据onLoad的方法的调用

【说明】【加载数据的时机】在页面切换时加载数据,因此需要监听;

【总结】

13.3 子类标签页网络请求数据

【说明】本身子类HomeFragment调用 LoadingPage加载数据的时候LoadData的时候已经在子线程中了;

 因此,在子类标签页网络加载数据的时候没有必要再次开辟子线程了;

【子类返回数据加载成功】【子类的数据加载成功之后的调用方法也是在主线程中】

【测试】应用界面设置的状态是成功;应用设置的状态是失败;游戏界面设置的状态是没有数据;

 14. 总结-参看视频

【说明】此篇内容是UI框架的搭建;

 

posted @ 2018-03-10 10:06  OzTaking  阅读(392)  评论(0)    收藏  举报