项目实战:硅谷金融APPday02学习笔记—主页及工具类创建
不否认努力,继续加油!
学习整理重点、盲区,笔记如下:干干巴巴,麻麻赖赖,一点都不圆润……
day02
内容
1. 提供欢迎页面
-
写布局;能于帧布局不用相对,能用相对不用线性;
-
代码实现,两秒后进入主应用;虽然动画写在后面,不过执行起来先执行动画,handler延迟后再进行 run();
protected void initData() { handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { startActivity(new Intent(WelcomeActivity.this,MainActivity.class)); finish(); } }, 2000); showAnimation(); }
-
设置为全屏显示
// 去掉窗口标题 requestWindowFeature(Window.FEATURE_NO_TITLE); // 隐藏顶部的状态栏 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_welcome);//写在后
2.提供Application子类、UIUtils工具类、联网配置AppNetConfig类、栈管理AppManager类
-
为什么提供MyApplication类?
Application类里获取的Context,Handler对象可以被当前Module里的任何类库引用到,而不必在需要用到的时候去new对象或者是通过构造传入。
Application类里获取的主线程和主线程 id 对象可以在当前 Module 任意的地方,判断当前代码是否运行在主线程当中,或者将一段代码指定在主线程中运行。
-
提供 MyApplication;
context = this.getApplicationContext(); handler = new Handler(); mainThread = Thread.currentThread();//实例化当前Application的线程即为主线程 mainThreadId = android.os.Process.myTid();//获取当前线程的id
-
提供 UIUtils
public static Context getContext() { return MyApplication.context; } public static Handler getHandler() { return MyApplication.handler; } public static View getView(int viewId) { View view = View.inflate(getContext(), viewId, null); return view; }
-
提供AppNetConfig类
用以配置联网请求网络的网络地址;方便直观的查看以及管理App中所有发起的服务器请求信息。
-
提供AppManager类
统一应用程序中所有的Activity的栈管理(单例);
关于六种单例模式的详细写法请看:6种单例模式.
涉及到activity的添加、删除指定、删除当前、删除所有、返回栈大小的方法。//单例模式:饿汉式 private ActivityManager(){ } private static ActivityManager activityManager = new ActivityManager(); public static ActivityManager getInstance(){ return activityManager; } //提供栈的对象 private Stack<Activity> activityStack = new Stack<>(); public void add(Activity activity){ if(activity != null){ activityStack.add(activity); } } public void remove(Activity activity){ if(activity != null){ for(int i = activityStack.size() - 1;i >= 0;i--){ Activity currentActivity = activityStack.get(i); if(currentActivity.getClass().equals(activity.getClass())){ currentActivity.finish();//销毁当前的activity activityStack.remove(i);//从栈空间移除 } } } } //删除当前的activity public void removeCurrent(){ //方式一: Activity activity = activityStack.get(activityStack.size() - 1); activity.finish(); activityStack.remove(activityStack.size() - 1); //方式二: Activity activity = activityStack.lastElement(); activity.finish(); activityStack.remove(activity); } //删除所有的activity public void removeAll(){ for (int i = activityStack.size() - 1;i >= 0;i--){ Activity activity = activityStack.get(i); activity.finish(); activityStack.remove(activity); } } //返回栈大小 public int size(){ return activityStack.size(); }
3. 未处理异常的全局捕获
-
用途:程序中的未捕获的全局异常的捕获(单例)
解决两个问题:
- 当出现未捕获的异常时,能够给用户一个相对友好的提示;下图是系统自带的,很难受……
- 在出现异常时,能够将异常信息发送给后台,便于在后续的版本中解决bug
- 当出现未捕获的异常时,能够给用户一个相对友好的提示;下图是系统自带的,很难受……
-
创建 CrashHandler.java
public class CrashHandler implements Thread.UncaughtExceptionHandler { //系统默认的处理未捕获异常的处理器 private Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler; //单例模式:(懒汉式) //本身实例化未捕获异常的处理器的操作就是系统在一个单独的线程中完成的,所有不涉及到 //多线程的问题,所以使用懒汉式更好! private CrashHandler(){ } private static CrashHandler crashHandler = null; public static CrashHandler getInstance(){ if(crashHandler == null){ crashHandler = new CrashHandler(); } return crashHandler; } public void init(){ defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); //将当前类设置为出现未捕获异常的处理器 Thread.setDefaultUncaughtExceptionHandler(this); } //一旦系统出现未捕获的异常,就会调用如下的回调方法 @Override public void uncaughtException(Thread thread, Throwable ex) { new Thread(){ @Override public void run(){ //Toast 必须在主线程;prepare()和loop()之间的操作就是在主线程中执行的! //在android系统中,默认情况下,一个线程中是不可以调用Looper进行消息的处理的。除非是主线程 Looper.prepare(); Toast.makeText(UIUtils.getContext(), "亲,出现了未捕获的异常了!", Toast.LENGTH_SHORT).show(); Looper.loop(); } }.start(); //收集异常信息 collectionException(ex); try { Thread.sleep(2000); //移除当前activity ActivityManager.getInstance().removeCurrent(); //结束当前的进程 android.os.Process.killProcess(android.os.Process.myPid()); //结束虚拟机 System.exit(0); } catch (InterruptedException e) { e.printStackTrace(); } } private void collectionException(Throwable ex){ final String exMessage = ex.getMessage(); //收集具体的客户的手机、系统的信息 final String message = Build.DEVICE + ":" + Build.MODEL + ":" + Build.PRODUCT + ":" + Build.VERSION.SDK_INT; //发送给后台此异常信息 new Thread(){ public void run(){ //需要按照指定的url,访问后台的sevlet,将异常信息发送过去 Log.e("TAG", "exception = " + exMessage); Log.e("TAG", "message = " + message); } }.start(); } }
-
上线后,在 MyApplication 中初始化;
4. ScrollView 控件的使用
- ScrollView 只可以有一个子布局;
5. 联网下载图片显示在 ViewPager
-
使用框架 ViewPagerIndicator 实现 ViewPager 的知识点,我们不写代码,我们只是代码的搬运工……
-
写布局,ViewPager 和他的指示器的关系,用帧布局比较合适;
<FrameLayout android:layout_width="match_parent" android:layout_height="230dp"> <androidx.viewpager.widget.ViewPager android:id="@+id/vp_home" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.viewpagerindicator.CirclePageIndicator android:id="@+id/cp_home_indicator" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal|bottom" android:layout_marginBottom="5dp" /> </FrameLayout>
-
使用 AsyncHttpClient 联网下载数据(OkHttp 也可,很主流);
-
使用picasso,联网下载图片;注意:使用Picasso会提供图片的缓存
-
CirclePageIndicator 关联 ViewPager:
circleIndicator.setViewPager(viewPager);
-
根据 Json 数据创建对应的 bean 对象解析 Json 字符串;
private void initData() { index = new Index(); AsyncHttpClient client = new AsyncHttpClient(); //访问的url String url = AppNetConfig.INDEX; client.post(url, new AsyncHttpResponseHandler() { @Override public void onSuccess(String content) {//200:响应成功 //解析json数据:GSON / FASTJSON JSONObject jsonObject = JSON.parseObject(content); //解析json对象数据 String proInfo = jsonObject.getString("proInfo"); Product product = JSON.parseObject(proInfo, Product.class); //解析json数组数据 String imageArr = jsonObject.getString("imageArr"); List<Image> images = JSON.parseArray(imageArr, Image.class); index.product = product; index.images = images; //更新页面数据 tvHomeProduct.setText(product.name); tvHomeYearrate.setText(product.yearRate + "%"); //设置ViewPager 或 banner 样式 …… } @Override public void onFailure(Throwable error, String content) { Toast.makeText(UIUtils.getContext(), "联网获取数据失败", Toast.LENGTH_SHORT).show(); } }); }
6. 用 banner 代替 ViewPager
-
添加依赖:
implementation 'com.youth.banner:banner:1.4.2'
;效果如下:
-
写布局
-
代码设置
//设置banner样式 banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE); //设置图片加载器 banner.setImageLoader(new GlideImageLoader()); //设置图片地址构成的集合 ArrayList<String> imagesUrl = new ArrayList<String>(index.images.size()); for (int i = 0; i < index.images.size(); i++) { imagesUrl.add(index.images.get(i).IMAURL); Log.i(TAG, "onSuccess: " + index.images.get(i).IMAURL); } banner.setImages(imagesUrl); //设置banner动画效果 banner.setBannerAnimation(Transformer.DepthPage); //设置标题集合(当banner样式有显示title时) String[] titles = new String[]{"分享砍学费", "人脉总动员", "想不到你是这样的app", "购物节,爱不单行"}; banner.setBannerTitles(Arrays.asList(titles)); banner.isAutoPlay(true); banner.setDelayTime(1500); //设置指示器位置(当banner模式中有指示器时) banner.setIndicatorGravity(BannerConfig.CENTER); //banner设置方法全部调用完毕时最后调用 banner.start(); // public class GlideImageLoader extends ImageLoader { @Override public void displayImage(Context context, Object path, ImageView imageView) { //Picasso 加载图片简单用法 Picasso.get().load((String) path).into(imageView); } }
盲区
- 声明:本博客根据尚硅谷项目实战: 硅谷金融.学习整理;
- 对于 MyApplication 的用途有了更深的理解;
- 对于 UiUtils 种获取所需属性的思维逻辑,有了新认识;
- 尤其是对于 栈管理,从未接触过,“先入后出”的思维还不够深入;
- 系统异常时,给用户友好提示:Toast 必须在主线程中;
- Looper.prepare();
//在主线程种操作
Looper.loop(); - AsyncHttpClient 请求下载数据是盲区;
其他笔记
金融App
- 金融APP01—页面架构.
- 金融APP02—主页及工具类创建
- 金融APP03—自定义 MyScrollView & 联网加载数据的4种状态的抽取及代码优化
商城
Android项目实战—— 商城APP.
新闻
Android项目实战—— 新闻APP.