日期: 2021 年 5 月 7 日

安卓性能优化之Fragment中数据的懒加载

1、背景:为什么需要懒加载。

我们在做安卓项目的时候,经常会有一个使用场景:ViewPage与多个Fragment组合使用。

%title插图%num

 

然而,viewpager有着预加载机制:默认一次加载当前页面前后两个页面,即使设置setOffLimit(0)也没有效果。  虽然预加载优化了app的体验效果,但是这样把我们看不到的页面的数据也加载了,大大降低了性能,浪费初始化资源。

这时候,我们就需要懒加载。

 

2、什么是懒加载:懒加载的定义。

当页面可见的时候,才加载当前页面。 没有打开的页面,就不会预加载。

说白了,懒加载就是可见的时候才去请求数据。

 

3、实现方法

(1)关键点

主要的方法是Fragment中的setUserVisibleHint(),此方法会在onCreateView()之前执行,当viewPager中fragment改变可见状态时也会调用,当fragment 从可见到不见,或者从不可见切换到可见,都会调用此方法,使用getUserVisibleHint() 可以返回fragment是否可见状态。

(2)注意事项

<1>添加isPrepared参数。在系统调用onActivityCreated时设置为true,这时onCreateView方法已调用完毕(一般我们在这方法里执行findviewbyid等方法),确保 onLazyLoad()方法不会报空指针异常。

<2>添加isLazyLoaded参数。确保ViewPager来回切换时BaseFragment的initData方法不会被重复调用,onLazyLoad在该Fragment的整个生命周期只调用一次,*次调用onLazyLoad()方法后马上执行 isLazyLoaded = true。

<3>getUserVisibleHint()会返回是否可见状态,这是fragment实现懒加载的关键,只有fragment 可见才会调用onLazyLoad() 加载数据。

 

(3)demo

%title插图%num
懒加载fragment基类

评论:

  • 码哥%title插图%numJaynm:兄弟,你这个还是有bug阿,如果跳页面切换时,还是会自动加载左右2个界面10 月前回复%title插图%num
    • 书中有颜如玉回复:我是这样处理的,这都过五个月了,你这还没处理吗5 月前回复%title插图%num
    • 书中有颜如玉回复:好久不见啊5 月前回复%title插图%num
    • 书中有颜如玉回复:

      1. /** Fragment当前状态是否可见 */
      2. protected boolean isVisible=false;
      3. @Override
      4. public void setUserVisibleHint(boolean isVisibleToUser) {
      5. super.setUserVisibleHint(isVisibleToUser);
      6. if(getUserVisibleHint()) {//可见
      7. isVisible = true;
      8. onVisible();
      9. } else {//不可见
      10. isVisible = false;
      11. onInvisible();
      12. }
      13. }

      5 月前回复%title插图%num

    • 码哥%title插图%numJaynm回复书中有颜如玉:有解决方案5 月前回复%title插图%num
    • 书中有颜如玉回复:和你遇到一样的问题了7 月前回复%title插图%num
    • 书中有颜如玉回复:你这个问题处理了吗7 月前回复

Android开发之多Fragment切换优化

问题分析

 


 

一直在简书里看别人的技术贴,今天我也来写点自己的心得!*近在写一个项目用到大量的Fragment后的总结!

我想刚刚接触安卓的同学或许会这么写:

  1. FragmentManager     fragmentManager=getSupportFragmentManager();
  2. FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
  3. fragmentTransaction.add(ViewId,fragment);// 或者fragmentTransaction.replace(ViewId,fragment);
  4. fragmentTransaction.commit();

基础更好一点的同学会用show和hide方法

  1.  FragmentManager fm = getSupportFragmentManager();
  2. FragmentTransaction ft = fm.beginTransaction();
  3. ft.hide(new FirstFragment())
  4.        .show(new SecondFragment())
  5.        .commit();

诚然这两种都可以切换Fragment,但是面对用户大量点击来回切换,或者你的Fragment本来就很多,每次都这样操作,那么很快你的应用就会OOM,就算不崩那也会异常的卡顿!so why?

当我们replace时发生了以下的生命周期:

 

%title插图%num

想想看每次都replace一下!!这世界会有多美好!!!那么问题出在哪?回过头看看代码就会发现每次在add/replace或者show/hide都会new 一个新的实例,这就是致命原因!!!!!

废话少说,开始优化

 


 

方案一:

预加载模式:

  1. //首先需要先实例好三个全局Fragment
  2. FragmentManager fm = getSupportFragmentManager();
  3. FragmentTransaction ft = fm.beginTransaction();
  4. ft.add(R.id.fragment, FirstFragment.getInstance());
  5. ft.add(R.id.fragment, SecondFragment.getInstance());
  6. ft.add(R.id.fragment, ThirdFragment.getInstance());
  7. ft.hide(SecondFragment.getInstance());
  8. ft.hide(ThirdFragment.getInstance());
  9. ft.commit();

在加载*个Fragment时就把全部Fragment加载好,下次使用直接调用如:

  1. FragmentManager fm = getSupportFragmentManager();
  2. FragmentTransaction ft = fm.beginTransaction();
  3. ft.hide(FirstFragment.getInstance())
  4.        .show(SecondFragment.getInstance())
  5.        .commit();

是不是总觉怪怪的,虽然比之前的代码好,但是这种做法很Java,当然需要预加载的朋友依然是不二之选!!!

那有没有更好的方法呢?答案是肯定的

方案二:

动态加载模式:

 


//首先需要先实例好n个全局Fragment

//private  Fragment  currentFragment=new Fragment();(全局)

 

private  FragmentTransaction switchFragment(Fragment targetFragment) {    FragmentTransaction transaction = getSupportFragmentManager()            .beginTransaction();    if (!targetFragment.isAdded()) {        //*次使用switchFragment()时currentFragment为null,所以要判断一下        if (currentFragment != null) {            transaction.hide(currentFragment);            }        transaction.add(R.id.fragment, targetFragment,targetFragment.getClass().getName());        } else {            transaction                    .hide(currentFragment)                    .show(targetFragment);        }        currentFragment = targetFragment;       return   transaction;    }

在点击切换Fragment时:

  1. @Override
  2. public void onTabSelected(@IdRes int tabId) {
  3.        if (tabId == R.id.tab_one){
  4.            switchFragment(first).commit();
  5.        }
  6.        if (tabId == R.id.tab_two){
  7.            switchFragment(second).commit();
  8.        }
  9.        if (tabId == R.id.tab_three){
  10.            switchFragment(third).commit();
  11.        }
  12.    }

现在你的Fragment无论怎么切都不会出现卡顿了,因为你的所有Fragment只会被实例化一次!实例一次的Fragment会被存入内存中,下次切换会判断内存中是否含有要切换的Fragment,如果有就直接复用,没有就add一个新的!优化大法完成!

外番

 


 

WHAT?等等!只实例一次,那我的Fragment里的数据要更新怎么办?我的回答是——软件关了再次重启!

 

%title插图%num

要是这样,这样的软件真的要逆天了!好在官方提供了onHiddenChanged方法,每次切换hide或者show时该方法会被执行,可以在这里面更新数据!


//此方法在Fragment中

@Override public void onHiddenChanged(boolean hidden) {    super.onHiddenChanged(hidden);    if (hidden){       //Fragment隐藏时调用    }else {        //Fragment显示时调用    } }

此方法是不是比每次add或replace更新数据执行一大坨的生命周期要优雅的多的多!

Android6.0权限申请

简单研究了一下Android6.0权限申请,在Google提供的sample的基础上,写了一个简单的demo。算是自己的笔记吧,可能会比较混乱,主要是方便以后查看。后期有别的问题,随时更新~

  • 本demo github下载地址!!!
  • Google提供的demo的下载地址
  • 6.0权限的基本知识,以下是需要单独申请的权限,共分为9组,每组只要有一个权限申请成功了,就默认整组权限都可以使用了。

    <pre class=”hljs undefined” data-original-code=”” group:android.permission-group.contacts”=”” data-snippet-id=”ext.92cf05e024a034db026f9fd16d3eb93f” data-snippet-saved=”false” data-codota-status=”done” style=”margin: 4px 0px; background-color: rgb(240, 240, 240);”>

    1. group:android.permission-group.CONTACTS
    2. permission:android.permission.WRITE_CONTACTS
    3. permission:android.permission.GET_ACCOUNTS
    4. permission:android.permission.READ_CONTACTS
    5. group:android.permission-group.PHONE
    6. permission:android.permission.READ_CALL_LOG
    7. permission:android.permission.READ_PHONE_STATE
    8. permission:android.permission.CALL_PHONE
    9. permission:android.permission.WRITE_CALL_LOG
    10. permission:android.permission.USE_SIP
    11. permission:android.permission.PROCESS_OUTGOING_CALLS
    12. permission:com.android.voicemail.permission.ADD_VOICEMAIL
    13. group:android.permission-group.CALENDAR
    14. permission:android.permission.READ_CALENDAR
    15. permission:android.permission.WRITE_CALENDAR
    16. group:android.permission-group.CAMERA
    17. permission:android.permission.CAMERA
    18. group:android.permission-group.SENSORS
    19. permission:android.permission.BODY_SENSORS
    20. group:android.permission-group.LOCATION
    21. permission:android.permission.ACCESS_FINE_LOCATION
    22. permission:android.permission.ACCESS_COARSE_LOCATION
    23. group:android.permission-group.STORAGE
    24. permission:android.permission.READ_EXTERNAL_STORAGE
    25. permission:android.permission.WRITE_EXTERNAL_STORAGE
    26. group:android.permission-group.MICROPHONE
    27. permission:android.permission.RECORD_AUDIO
    28. group:android.permission-group.SMS
    29. permission:android.permission.READ_SMS
    30. permission:android.permission.RECEIVE_WAP_PUSH
    31. permission:android.permission.RECEIVE_MMS
    32. permission:android.permission.RECEIVE_SMS
    33. permission:android.permission.SEND_SMS
    34. permission:android.permission.READ_CELL_BROADCASTS
  • 以下是普通权限,只需要在AndroidManifest.xml中申请即可。
    1. android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
    2. android.permission.ACCESS_NETWORK_STATE
    3. android.permission.ACCESS_NOTIFICATION_POLICY
    4. android.permission.ACCESS_WIFI_STATE
    5. android.permission.ACCESS_WIMAX_STATE
    6. android.permission.BLUETOOTH
    7. android.permission.BLUETOOTH_ADMIN
    8. android.permission.BROADCAST_STICKY
    9. android.permission.CHANGE_NETWORK_STATE
    10. android.permission.CHANGE_WIFI_MULTICAST_STATE
    11. android.permission.CHANGE_WIFI_STATE
    12. android.permission.CHANGE_WIMAX_STATE
    13. android.permission.DISABLE_KEYGUARD
    14. android.permission.EXPAND_STATUS_BAR
    15. android.permission.FLASHLIGHT
    16. android.permission.GET_ACCOUNTS
    17. android.permission.GET_PACKAGE_SIZE
    18. android.permission.INTERNET
    19. android.permission.KILL_BACKGROUND_PROCESSES
    20. android.permission.MODIFY_AUDIO_SETTINGS
    21. android.permission.NFC
    22. android.permission.READ_SYNC_SETTINGS
    23. android.permission.READ_SYNC_STATS
    24. android.permission.RECEIVE_BOOT_COMPLETED
    25. android.permission.REORDER_TASKS
    26. android.permission.REQUEST_INSTALL_PACKAGES
    27. android.permission.SET_TIME_ZONE
    28. android.permission.SET_WALLPAPER
    29. android.permission.SET_WALLPAPER_HINTS
    30. android.permission.SUBSCRIBED_FEEDS_READ
    31. android.permission.TRANSMIT_IR
    32. android.permission.USE_FINGERPRINT
    33. android.permission.VIBRATE
    34. android.permission.WAKE_LOCK
    35. android.permission.WRITE_SYNC_SETTINGS
    36. com.android.alarm.permission.SET_ALARM
    37. com.android.launcher.permission.INSTALL_SHORTCUT
    38. com.android.launcher.permission.UNINSTALL_SHORTCUT

申请步骤

    1. 将targetSdkVersion设置为23,注意,如果你将targetSdkVersion设置为>=23,则必须按照Android谷歌的要求,动态的申请权限,如果你暂时不打算支持动态权限申请,则targetSdkVersion*大只能设置为22.
  • 2 在AndroidManifest.xml中申请你需要的权限,包括普通权限和需要申请的特殊权限。
  • 3.开始申请权限,此处分为3部。
    • (1)检查是否由此权限checkSelfPermission(),如果已经开启,则直接做你想做的。
    • (2)如果未开启,则判断是否需要向用户解释为何申请权限shouldShowRequestPermissionRationale。
    • (3)如果需要(即返回true),则可以弹出对话框提示用户申请权限原因,用户确认后申请权限requestPermissions(),如果不需要(即返回false),则直接申请权限requestPermissions()。 (这里是一部门代码,底部有比较完善的代码,整个demo可以在github中下载)。
%title插图%num

单个权限申请.png
  1. /**
  2. * Requests permission.
  3. *
  4. * @param activity
  5. * @param requestCode request code, e.g. if you need request CAMERA permission,parameters is PermissionUtils.CODE_CAMERA
  6. */
  7. public static void requestPermission(final Activity activity, final int requestCode, PermissionGrant permissionGrant) {
  8. if (activity == null) {
  9. return;
  10. }
  11. Log.i(TAG, “requestPermission requestCode:” + requestCode);
  12. if (requestCode < 0 || requestCode >= requestPermissions.length) {
  13. Log.w(TAG, “requestPermission illegal requestCode:” + requestCode);
  14. return;
  15. }
  16. final String requestPermission = requestPermissions[requestCode];
  17. //如果是6.0以下的手机,ActivityCompat.checkSelfPermission()会始终等于PERMISSION_GRANTED,
  18. // 但是,如果用户关闭了你申请的权限(如下图,在安装的时候,将一些权限关闭了),ActivityCompat.checkSelfPermission()则可能会导致程序崩溃(java.lang.RuntimeException: Unknown exception code: 1 msg null),
  19. // 你可以使用try{}catch(){},处理异常,也可以判断系统版本,低于23就不申请权限,直接做你想做的。permissionGrant.onPermissionGranted(requestCode);
  20. // if (Build.VERSION.SDK_INT < 23) {
  21. // permissionGrant.onPermissionGranted(requestCode);
  22. // return;
  23. // }
  24. int checkSelfPermission;
  25. try {
  26. checkSelfPermission = ActivityCompat.checkSelfPermission(activity, requestPermission);
  27. } catch (RuntimeException e) {
  28. Toast.makeText(activity, “please open this permission”, Toast.LENGTH_SHORT)
  29. .show();
  30. Log.e(TAG, “RuntimeException:” + e.getMessage());
  31. return;
  32. }
  33. if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) {
  34. Log.i(TAG, “ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED”);
  35. if (ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission)) {
  36. Log.i(TAG, “requestPermission shouldShowRequestPermissionRationale”);
  37. shouldShowRationale(activity, requestCode, requestPermission);
  38. } else {
  39. Log.d(TAG, “requestCameraPermission else”);
  40. ActivityCompat.requestPermissions(activity, new String[]{requestPermission}, requestCode);
  41. }
  42. } else {
  43. Log.d(TAG, “ActivityCompat.checkSelfPermission ==== PackageManager.PERMISSION_GRANTED”);
  44. Toast.makeText(activity, “opened:” + requestPermissions[requestCode], Toast.LENGTH_SHORT).show();
  45. //得到权限的时候,就可以在回调里面做你想做的事情了
  46. permissionGrant.onPermissionGranted(requestCode);
  47. }
  48. }
%title插图%num

6.0以下系统的应用程序安装界面.png

备注!!!

(1)checkSelfPermission:检查是否拥有这个权限 (2)requestPermissions:请求权限,一般会弹出一个系统对话框,询问用户是否开启这个权限。 (3)shouldShowRequestPermissionRationale:Android原生系统中,如果第二次弹出权限申请的对话框,会出现“以后不再弹出”的提示框,如果用户勾选了,你再申请权限,则shouldShowRequestPermissionRationale返回true,意思是说要给用户一个 解释,告诉用户为什么要这个权限。然而,在实际开发中,需要注意的是,很多手机对原生系统做了修改,比如小米,小米4的6.0的shouldShowRequestPermissionRationale 就一直返回false,而且在申请权限时,如果用户选择了拒*,则不会再弹出对话框了。。。。 所以说这个地方有坑,我的解决方法是,在回调里面处理,如果用户拒*了这个权限,则打开本应用信息界面,由用户自己手动开启这个权限。 (4)每个应用都有自己的权限管理界面,里面有本应用申请的权限以及各种状态,即使用户已经同意了你申请的权限,他也随时可以关闭

%title插图%num

权限管理界面.png

一次申请多个权限

其实和申请一个权限是一样的,只是requestPermissions(final @NonNull Activity activity, final @NonNull String[] permissions, final int requestCode),里面的permissions给的参数多些而已。

%title插图%num

申请多个权限.png
  1. /**
  2. * 一次申请多个权限
  3. */
  4. public static void requestMultiPermissions(final Activity activity, PermissionGrant grant) {
  5. final List<String> permissionsList = getNoGrantedPermission(activity, false);
  6. final List<String> shouldRationalePermissionsList = getNoGrantedPermission(activity, true);
  7. //TODO checkSelfPermission
  8. if (permissionsList == null || shouldRationalePermissionsList == null) {
  9. return;
  10. }
  11. Log.d(TAG, “requestMultiPermissions permissionsList:” + permissionsList.size() + “,shouldRationalePermissionsList:” + shouldRationalePermissionsList.size());
  12. if (permissionsList.size() > 0) {
  13. ActivityCompat.requestPermissions(activity, permissionsList.toArray(new String[permissionsList.size()]),
  14. CODE_MULTI_PERMISSION);
  15. Log.d(TAG, “showMessageOKCancel requestPermissions”);
  16. } else if (shouldRationalePermissionsList.size() > 0) {
  17. showMessageOKCancel(activity, “should open those permission”,
  18. new DialogInterface.OnClickListener() {
  19. @Override
  20. public void onClick(DialogInterface dialog, int which) {
  21. ActivityCompat.requestPermissions(activity, shouldRationalePermissionsList.toArray(new String[shouldRationalePermissionsList.size()]),
  22. CODE_MULTI_PERMISSION);
  23. Log.d(TAG, “showMessageOKCancel requestPermissions”);
  24. }
  25. });
  26. } else {
  27. grant.onPermissionGranted(CODE_MULTI_PERMISSION);
  28. }
  29. }
  • 关于权限请求结果的回调。Activity实现ActivityCompat.OnRequestPermissionsResultCallback接口,重写onRequestPermissionsResult方法。
    1. @Override
    2. public void onRequestPermissionsResult(final int requestCode, @NonNull String[] permissions,
    3. @NonNull int[] grantResults) {
    4. PermissionUtils.requestPermissionsResult(this, requestCode, permissions, grantResults, mPermissionGrant);
    5. }

整个申请权限工具类代码

  1. package com.example.android.system.runtimepermissions;
  2. import android.Manifest;
  3. import android.app.Activity;
  4. import android.content.DialogInterface;
  5. import android.content.Intent;
  6. import android.content.pm.PackageManager;
  7. import android.net.Uri;
  8. import android.provider.Settings;
  9. import android.support.annotation.NonNull;
  10. import android.support.v4.app.ActivityCompat;
  11. import android.support.v7.app.AlertDialog;
  12. import android.util.Log;
  13. import android.widget.Toast;
  14. import java.util.ArrayList;
  15. import java.util.HashMap;
  16. import java.util.List;
  17. import java.util.Map;
  18. /**
  19. * Created by qianxiaoai on 2016/7/7.
  20. */
  21. public class PermissionUtils {
  22. private static final String TAG = PermissionUtils.class.getSimpleName();
  23. public static final int CODE_RECORD_AUDIO = 0;
  24. public static final int CODE_GET_ACCOUNTS = 1;
  25. public static final int CODE_READ_PHONE_STATE = 2;
  26. public static final int CODE_CALL_PHONE = 3;
  27. public static final int CODE_CAMERA = 4;
  28. public static final int CODE_ACCESS_FINE_LOCATION = 5;
  29. public static final int CODE_ACCESS_COARSE_LOCATION = 6;
  30. public static final int CODE_READ_EXTERNAL_STORAGE = 7;
  31. public static final int CODE_WRITE_EXTERNAL_STORAGE = 8;
  32. public static final int CODE_MULTI_PERMISSION = 100;
  33. public static final String PERMISSION_RECORD_AUDIO = Manifest.permission.RECORD_AUDIO;
  34. public static final String PERMISSION_GET_ACCOUNTS = Manifest.permission.GET_ACCOUNTS;
  35. public static final String PERMISSION_READ_PHONE_STATE = Manifest.permission.READ_PHONE_STATE;
  36. public static final String PERMISSION_CALL_PHONE = Manifest.permission.CALL_PHONE;
  37. public static final String PERMISSION_CAMERA = Manifest.permission.CAMERA;
  38. public static final String PERMISSION_ACCESS_FINE_LOCATION = Manifest.permission.ACCESS_FINE_LOCATION;
  39. public static final String PERMISSION_ACCESS_COARSE_LOCATION = Manifest.permission.ACCESS_COARSE_LOCATION;
  40. public static final String PERMISSION_READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE;
  41. public static final String PERMISSION_WRITE_EXTERNAL_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE;
  42. private static final String[] requestPermissions = {
  43. PERMISSION_RECORD_AUDIO,
  44. PERMISSION_GET_ACCOUNTS,
  45. PERMISSION_READ_PHONE_STATE,
  46. PERMISSION_CALL_PHONE,
  47. PERMISSION_CAMERA,
  48. PERMISSION_ACCESS_FINE_LOCATION,
  49. PERMISSION_ACCESS_COARSE_LOCATION,
  50. PERMISSION_READ_EXTERNAL_STORAGE,
  51. PERMISSION_WRITE_EXTERNAL_STORAGE
  52. };
  53. interface PermissionGrant {
  54. void onPermissionGranted(int requestCode);
  55. }
  56. /**
  57. * Requests permission.
  58. *
  59. * @param activity
  60. * @param requestCode request code, e.g. if you need request CAMERA permission,parameters is PermissionUtils.CODE_CAMERA
  61. */
  62. public static void requestPermission(final Activity activity, final int requestCode, PermissionGrant permissionGrant) {
  63. if (activity == null) {
  64. return;
  65. }
  66. Log.i(TAG, “requestPermission requestCode:” + requestCode);
  67. if (requestCode < 0 || requestCode >= requestPermissions.length) {
  68. Log.w(TAG, “requestPermission illegal requestCode:” + requestCode);
  69. return;
  70. }
  71. final String requestPermission = requestPermissions[requestCode];
  72. //如果是6.0以下的手机,ActivityCompat.checkSelfPermission()会始终等于PERMISSION_GRANTED,
  73. // 但是,如果用户关闭了你申请的权限,ActivityCompat.checkSelfPermission(),会导致程序崩溃(java.lang.RuntimeException: Unknown exception code: 1 msg null),
  74. // 你可以使用try{}catch(){},处理异常,也可以在这个地方,低于23就什么都不做,
  75. // 个人建议try{}catch(){}单独处理,提示用户开启权限。
  76. // if (Build.VERSION.SDK_INT < 23) {
  77. // return;
  78. // }
  79. int checkSelfPermission;
  80. try {
  81. checkSelfPermission = ActivityCompat.checkSelfPermission(activity, requestPermission);
  82. } catch (RuntimeException e) {
  83. Toast.makeText(activity, “please open this permission”, Toast.LENGTH_SHORT)
  84. .show();
  85. Log.e(TAG, “RuntimeException:” + e.getMessage());
  86. return;
  87. }
  88. if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) {
  89. Log.i(TAG, “ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED”);
  90. if (ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission)) {
  91. Log.i(TAG, “requestPermission shouldShowRequestPermissionRationale”);
  92. shouldShowRationale(activity, requestCode, requestPermission);
  93. } else {
  94. Log.d(TAG, “requestCameraPermission else”);
  95. ActivityCompat.requestPermissions(activity, new String[]{requestPermission}, requestCode);
  96. }
  97. } else {
  98. Log.d(TAG, “ActivityCompat.checkSelfPermission ==== PackageManager.PERMISSION_GRANTED”);
  99. Toast.makeText(activity, “opened:” + requestPermissions[requestCode], Toast.LENGTH_SHORT).show();
  100. permissionGrant.onPermissionGranted(requestCode);
  101. }
  102. }
  103. private static void requestMultiResult(Activity activity, String[] permissions, int[] grantResults, PermissionGrant permissionGrant) {
  104. if (activity == null) {
  105. return;
  106. }
  107. //TODO
  108. Log.d(TAG, “onRequestPermissionsResult permissions length:” + permissions.length);
  109. Map<String, Integer> perms = new HashMap<>();
  110. ArrayList<String> notGranted = new ArrayList<>();
  111. for (int i = 0; i < permissions.length; i++) {
  112. Log.d(TAG, “permissions: [i]:” + i + “, permissions[i]” + permissions[i] + “,grantResults[i]:” + grantResults[i]);
  113. perms.put(permissions[i], grantResults[i]);
  114. if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
  115. notGranted.add(permissions[i]);
  116. }
  117. }
  118. if (notGranted.size() == 0) {
  119. Toast.makeText(activity, “all permission success” + notGranted, Toast.LENGTH_SHORT)
  120. .show();
  121. permissionGrant.onPermissionGranted(CODE_MULTI_PERMISSION);
  122. } else {
  123. openSettingActivity(activity, “those permission need granted!”);
  124. }
  125. }
  126. /**
  127. * 一次申请多个权限
  128. */
  129. public static void requestMultiPermissions(final Activity activity, PermissionGrant grant) {
  130. final List<String> permissionsList = getNoGrantedPermission(activity, false);
  131. final List<String> shouldRationalePermissionsList = getNoGrantedPermission(activity, true);
  132. //TODO checkSelfPermission
  133. if (permissionsList == null || shouldRationalePermissionsList == null) {
  134. return;
  135. }
  136. Log.d(TAG, “requestMultiPermissions permissionsList:” + permissionsList.size() + “,shouldRationalePermissionsList:” + shouldRationalePermissionsList.size());
  137. if (permissionsList.size() > 0) {
  138. ActivityCompat.requestPermissions(activity, permissionsList.toArray(new String[permissionsList.size()]),
  139. CODE_MULTI_PERMISSION);
  140. Log.d(TAG, “showMessageOKCancel requestPermissions”);
  141. } else if (shouldRationalePermissionsList.size() > 0) {
  142. showMessageOKCancel(activity, “should open those permission”,
  143. new DialogInterface.OnClickListener() {
  144. @Override
  145. public void onClick(DialogInterface dialog, int which) {
  146. ActivityCompat.requestPermissions(activity, shouldRationalePermissionsList.toArray(new String[shouldRationalePermissionsList.size()]),
  147. CODE_MULTI_PERMISSION);
  148. Log.d(TAG, “showMessageOKCancel requestPermissions”);
  149. }
  150. });
  151. } else {
  152. grant.onPermissionGranted(CODE_MULTI_PERMISSION);
  153. }
  154. }
  155. private static void shouldShowRationale(final Activity activity, final int requestCode, final String requestPermission) {
  156. //TODO
  157. String[] permissionsHint = activity.getResources().getStringArray(R.array.permissions);
  158. showMessageOKCancel(activity, “Rationale: “ + permissionsHint[requestCode], new DialogInterface.OnClickListener() {
  159. @Override
  160. public void onClick(DialogInterface dialog, int which) {
  161. ActivityCompat.requestPermissions(activity,
  162. new String[]{requestPermission},
  163. requestCode);
  164. Log.d(TAG, “showMessageOKCancel requestPermissions:” + requestPermission);
  165. }
  166. });
  167. }
  168. private static void showMessageOKCancel(final Activity context, String message, DialogInterface.OnClickListener okListener) {
  169. new AlertDialog.Builder(context)
  170. .setMessage(message)
  171. .setPositiveButton(“OK”, okListener)
  172. .setNegativeButton(“Cancel”, null)
  173. .create()
  174. .show();
  175. }
  176. /**
  177. * @param activity
  178. * @param requestCode Need consistent with requestPermission
  179. * @param permissions
  180. * @param grantResults
  181. */
  182. public static void requestPermissionsResult(final Activity activity, final int requestCode, @NonNull String[] permissions,
  183. @NonNull int[] grantResults, PermissionGrant permissionGrant) {
  184. if (activity == null) {
  185. return;
  186. }
  187. Log.d(TAG, “requestPermissionsResult requestCode:” + requestCode);
  188. if (requestCode == CODE_MULTI_PERMISSION) {
  189. requestMultiResult(activity, permissions, grantResults, permissionGrant);
  190. return;
  191. }
  192. if (requestCode < 0 || requestCode >= requestPermissions.length) {
  193. Log.w(TAG, “requestPermissionsResult illegal requestCode:” + requestCode);
  194. Toast.makeText(activity, “illegal requestCode:” + requestCode, Toast.LENGTH_SHORT).show();
  195. return;
  196. }
  197. Log.i(TAG, “onRequestPermissionsResult requestCode:” + requestCode + “,permissions:” + permissions.toString()
  198. + “,grantResults:” + grantResults.toString() + “,length:” + grantResults.length);
  199. if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  200. Log.i(TAG, “onRequestPermissionsResult PERMISSION_GRANTED”);
  201. //TODO success, do something, can use callback
  202. permissionGrant.onPermissionGranted(requestCode);
  203. } else {
  204. //TODO hint user this permission function
  205. Log.i(TAG, “onRequestPermissionsResult PERMISSION NOT GRANTED”);
  206. //TODO
  207. String[] permissionsHint = activity.getResources().getStringArray(R.array.permissions);
  208. openSettingActivity(activity, “Result” + permissionsHint[requestCode]);
  209. }
  210. }
  211. private static void openSettingActivity(final Activity activity, String message) {
  212. showMessageOKCancel(activity, message, new DialogInterface.OnClickListener() {
  213. @Override
  214. public void onClick(DialogInterface dialog, int which) {
  215. Intent intent = new Intent();
  216. intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
  217. Log.d(TAG, “getPackageName(): “ + activity.getPackageName());
  218. Uri uri = Uri.fromParts(“package”, activity.getPackageName(), null);
  219. intent.setData(uri);
  220. activity.startActivity(intent);
  221. }
  222. });
  223. }
  224. /**
  225. * @param activity
  226. * @param isShouldRationale true: return no granted and shouldShowRequestPermissionRationale permissions, false:return no granted and !shouldShowRequestPermissionRationale
  227. * @return
  228. */
  229. public static ArrayList<String> getNoGrantedPermission(Activity activity, boolean isShouldRationale) {
  230. ArrayList<String> permissions = new ArrayList<>();
  231. for (int i = 0; i < requestPermissions.length; i++) {
  232. String requestPermission = requestPermissions[i];
  233. //TODO checkSelfPermission
  234. int checkSelfPermission = -1;
  235. try {
  236. checkSelfPermission = ActivityCompat.checkSelfPermission(activity, requestPermission);
  237. } catch (RuntimeException e) {
  238. Toast.makeText(activity, “please open those permission”, Toast.LENGTH_SHORT)
  239. .show();
  240. Log.e(TAG, “RuntimeException:” + e.getMessage());
  241. return null;
  242. }
  243. if (checkSelfPermission != PackageManager.PERMISSION_GRANTED) {
  244. Log.i(TAG, “getNoGrantedPermission ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED:” + requestPermission);
  245. if (ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission)) {
  246. Log.d(TAG, “shouldShowRequestPermissionRationale if”);
  247. if (isShouldRationale) {
  248. permissions.add(requestPermission);
  249. }
  250. } else {
  251. if (!isShouldRationale) {
  252. permissions.add(requestPermission);
  253. }
  254. Log.d(TAG, “shouldShowRequestPermissionRationale else”);
  255. }
  256. }
  257. }
  258. return permissions;
  259. }
  260. }

界面调用代码

  1. package com.example.android.system.runtimepermissions;
  2. import android.os.Bundle;
  3. import android.support.annotation.NonNull;
  4. import android.support.v4.app.ActivityCompat;
  5. import android.support.v4.app.FragmentActivity;
  6. import android.support.v4.app.FragmentTransaction;
  7. import android.view.View;
  8. import android.widget.Toast;
  9. import com.example.android.common.logger.Log;
  10. /**
  11. * Created by qianxiaoai on 2016/7/8.
  12. */
  13. public class PermissionActivity extends FragmentActivity implements ActivityCompat.OnRequestPermissionsResultCallback{
  14. private static final String TAG = PermissionActivity.class.getSimpleName();
  15. @Override
  16. public void onCreate(Bundle savedInstanceState) {
  17. super.onCreate(savedInstanceState);
  18. setContentView(R.layout.activity_permission);
  19. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
  20. PermissionsFragment fragment = new PermissionsFragment();
  21. transaction.replace(R.id.content_fragment, fragment);
  22. transaction.commit();
  23. }
  24. /**
  25. * Called when the ‘show camera’ button is clicked.
  26. * Callback is defined in resource layout definition.
  27. */
  28. public void showCamera(View view) {
  29. Log.i(TAG, “Show camera button pressed. Checking permission.”);
  30. PermissionUtils.requestPermission(this, PermissionUtils.CODE_CAMERA, mPermissionGrant);
  31. }
  32. public void getAccounts(View view) {
  33. PermissionUtils.requestPermission(this, PermissionUtils.CODE_GET_ACCOUNTS, mPermissionGrant);
  34. }
  35. public void callPhone(View view) {
  36. PermissionUtils.requestPermission(this, PermissionUtils.CODE_CALL_PHONE, mPermissionGrant);
  37. }
  38. public void readPhoneState(View view) {
  39. PermissionUtils.requestPermission(this, PermissionUtils.CODE_READ_PHONE_STATE, mPermissionGrant);
  40. }
  41. public void accessFineLocation(View view) {
  42. PermissionUtils.requestPermission(this, PermissionUtils.CODE_ACCESS_FINE_LOCATION, mPermissionGrant);
  43. }
  44. public void accessCoarseLocation(View view) {
  45. PermissionUtils.requestPermission(this, PermissionUtils.CODE_ACCESS_COARSE_LOCATION, mPermissionGrant);
  46. }
  47. public void readExternalStorage(View view) {
  48. PermissionUtils.requestPermission(this, PermissionUtils.CODE_READ_EXTERNAL_STORAGE, mPermissionGrant);
  49. }
  50. public void writeExternalStorage(View view) {
  51. PermissionUtils.requestPermission(this, PermissionUtils.CODE_WRITE_EXTERNAL_STORAGE, mPermissionGrant);
  52. }
  53. public void recordAudio(View view) {
  54. PermissionUtils.requestPermission(this, PermissionUtils.CODE_RECORD_AUDIO, mPermissionGrant);
  55. }
  56. private PermissionUtils.PermissionGrant mPermissionGrant = new PermissionUtils.PermissionGrant() {
  57. @Override
  58. public void onPermissionGranted(int requestCode) {
  59. switch (requestCode) {
  60. case PermissionUtils.CODE_RECORD_AUDIO:
  61. Toast.makeText(PermissionActivity.this, “Result Permission Grant CODE_RECORD_AUDIO”, Toast.LENGTH_SHORT).show();
  62. break;
  63. case PermissionUtils.CODE_GET_ACCOUNTS:
  64. Toast.makeText(PermissionActivity.this, “Result Permission Grant CODE_GET_ACCOUNTS”, Toast.LENGTH_SHORT).show();
  65. break;
  66. case PermissionUtils.CODE_READ_PHONE_STATE:
  67. Toast.makeText(PermissionActivity.this, “Result Permission Grant CODE_READ_PHONE_STATE”, Toast.LENGTH_SHORT).show();
  68. break;
  69. case PermissionUtils.CODE_CALL_PHONE:
  70. Toast.makeText(PermissionActivity.this, “Result Permission Grant CODE_CALL_PHONE”, Toast.LENGTH_SHORT).show();
  71. break;
  72. case PermissionUtils.CODE_CAMERA:
  73. Toast.makeText(PermissionActivity.this, “Result Permission Grant CODE_CAMERA”, Toast.LENGTH_SHORT).show();
  74. break;
  75. case PermissionUtils.CODE_ACCESS_FINE_LOCATION:
  76. Toast.makeText(PermissionActivity.this, “Result Permission Grant CODE_ACCESS_FINE_LOCATION”, Toast.LENGTH_SHORT).show();
  77. break;
  78. case PermissionUtils.CODE_ACCESS_COARSE_LOCATION:
  79. Toast.makeText(PermissionActivity.this, “Result Permission Grant CODE_ACCESS_COARSE_LOCATION”, Toast.LENGTH_SHORT).show();
  80. break;
  81. case PermissionUtils.CODE_READ_EXTERNAL_STORAGE:
  82. Toast.makeText(PermissionActivity.this, “Result Permission Grant CODE_READ_EXTERNAL_STORAGE”, Toast.LENGTH_SHORT).show();
  83. break;
  84. case PermissionUtils.CODE_WRITE_EXTERNAL_STORAGE:
  85. Toast.makeText(PermissionActivity.this, “Result Permission Grant CODE_WRITE_EXTERNAL_STORAGE”, Toast.LENGTH_SHORT).show();
  86. break;
  87. default:
  88. break;
  89. }
  90. }
  91. };
  92. /**
  93. * Callback received when a permissions request has been completed.
  94. */
  95. @Override
  96. public void onRequestPermissionsResult(final int requestCode, @NonNull String[] permissions,
  97. @NonNull int[] grantResults) {
  98. PermissionUtils.requestPermissionsResult(this, requestCode, permissions, grantResults, mPermissionGrant);
  99. }
  100. }

xml布局

  1. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  2. android:layout_width=“match_parent”
  3. android:layout_height=“match_parent”
  4. android:paddingLeft=“@dimen/horizontal_page_margin”
  5. android:paddingRight=“@dimen/horizontal_page_margin”
  6. android:paddingTop=“@dimen/vertical_page_margin”
  7. android:paddingBottom=“@dimen/vertical_page_margin”
  8. android:orientation=“vertical”
  9. >
  10. <FrameLayout
  11. android:id=“@+id/content_fragment”
  12. android:layout_width=“match_parent”
  13. android:layout_height=“0dp”
  14. android:layout_weight=“1”/>
  15. <ScrollView
  16. android:layout_width=“match_parent”
  17. android:layout_height=“0dp”
  18. android:layout_weight=“1”>
  19. <LinearLayout
  20. android:layout_width=“match_parent”
  21. android:layout_height=“match_parent”
  22. android:orientation=“vertical”>
  23. <LinearLayout
  24. android:layout_width=“match_parent”
  25. android:layout_height=“wrap_content”
  26. android:orientation=“horizontal”>
  27. <Button
  28. android:layout_width=“wrap_content”
  29. android:layout_height=“wrap_content”
  30. android:text=“Camera”
  31. android:id=“@+id/button_camera”
  32. android:onClick=“showCamera”/>
  33. <Button
  34. android:layout_width=“wrap_content”
  35. android:layout_height=“wrap_content”
  36. android:text=“RECORD_AUDIO”
  37. android:onClick=“recordAudio”/>
  38. </LinearLayout>
  39. <LinearLayout
  40. android:layout_width=“match_parent”
  41. android:layout_height=“wrap_content”
  42. android:orientation=“horizontal”>
  43. <Button
  44. android:layout_width=“wrap_content”
  45. android:layout_height=“wrap_content”
  46. android:text=“GET_ACCOUNTS”
  47. android:onClick=“getAccounts”/>
  48. <Button
  49. android:layout_width=“wrap_content”
  50. android:layout_height=“wrap_content”
  51. android:text=“CALL_PHONE”
  52. android:onClick=“callPhone”/>
  53. </LinearLayout>
  54. <Button
  55. android:layout_width=“wrap_content”
  56. android:layout_height=“wrap_content”
  57. android:text=“PERMISSION_READ_PHONE_STATE”
  58. android:onClick=“readPhoneState”/>
  59. <Button
  60. android:layout_width=“wrap_content”
  61. android:layout_height=“wrap_content”
  62. android:text=“ACCESS_FINE_LOCATION”
  63. android:onClick=“accessFineLocation”/>
  64. <Button
  65. android:layout_width=“wrap_content”
  66. android:layout_height=“wrap_content”
  67. android:text=“ACCESS_COARSE_LOCATION”
  68. android:onClick=“accessCoarseLocation”/>
  69. <Button
  70. android:layout_width=“wrap_content”
  71. android:layout_height=“wrap_content”
  72. android:text=“READ_EXTERNAL_STORAGE”
  73. android:onClick=“readExternalStorage”/>
  74. <Button
  75. android:layout_width=“wrap_content”
  76. android:layout_height=“wrap_content”
  77. android:text=“WRITE_EXTERNAL_STORAGE”
  78. android:onClick=“writeExternalStorage”/>
  79. </LinearLayout>
  80. </ScrollView>
  81. </LinearLayout>

清单文件申请的权限

  1. <uses-permission android:name=“android.permission.CAMERA”/>
  2. <uses-permission android:name=“android.permission.ACCESS_FINE_LOCATION”/>
  3. <uses-permission android:name=“android.permission.ACCESS_COARSE_LOCATION”/>
  4. <uses-permission android:name=“android.permission.CALL_PHONE”/>
  5. <uses-permission android:name=“android.permission.SEND_SMS”/>
  6. <uses-permission android:name=“android.permission.READ_SMS”/>
  7. <uses-permission android:name=“android.permission.GET_ACCOUNTS”/>
  8. <uses-permission android:name=“android.permission.READ_PHONE_STATE”/>
  9. <uses-permission android:name=“android.permission.READ_EXTERNAL_STORAGE”/>
  10. <uses-permission android:name=“android.permission.WRITE_EXTERNAL_STORAGE”/>
  11. <uses-permission android:name=“android.permission.RECORD_AUDIO”/>

部分 资源文件

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2. <resources>
  3. <string-array name=“permissions”>
  4. <item>@string/permission_recode_audio_hint</item>
  5. <item>@string/permission_get_accounts_hint</item>
  6. <item>@string/permission_read_phone_hint</item>
  7. <item>@string/permission_call_phone_hint</item>
  8. <item>@string/permission_camera_hint</item>
  9. <item>@string/permission_access_fine_location_hint</item>
  10. <item>@string/permission_access_coarse_location_hint</item>
  11. <item>@string/permission_read_external_hint</item>
  12. <item>@string/permission_white_external_hint</item>
  13. </string-array>
  14. </resources>
  15. <string name=“permission_get_accounts_hint”>没有此权限,无法开启这个功能,请开启权限。PERMISSION_GET_ACCOUNTS</string>
  16. <string name=“permission_read_phone_hint”>没有此权限,无法开启这个功能,请开启权限。PERMISSION_READ_PHONE_STATE</string>
  17. <string name=“permission_call_phone_hint”>没有此权限,无法开启这个功能,请开启权限。PERMISSION_CALL_PHONE</string>
  18. <string name=“permission_camera_hint”>没有此权限,无法开启这个功能,请开启权限。PERMISSION_CAMERA</string>
  19. <string name=“permission_access_fine_location_hint”>没有此权限,无法开启这个功能,请开启权限。PERMISSION_ACCESS_FINE_LOCATION</string>
  20. <string name=“permission_access_coarse_location_hint”>没有此权限,无法开启这个功能,请开启权限。PERMISSION_ACCESS_COARSE_LOCATION</string>
  21. <string name=“permission_read_external_hint”>没有此权限,无法开启这个功能,请开启权限。PERMISSION_READ_EXTERNAL_STORAGE</string>
  22. <string name=“permission_white_external_hint”>没有此权限,无法开启这个功能,请开启权限。PERMISSION_WRITE_EXTERNAL_STORAGE</string>
  23. <string name=“permission_recode_audio_hint”>没有此权限,无法开启这个功能,请开启权限。PERMISSION_RECORD_AUDIO</string>

Android共享元素转场动画Part2——Fragment to Fragment

继续Part1部分–Activity to Activity,这个部分我们简单介绍下Fragment to Fragment的共享动画实现;

Fragment to Fragment

首先我们需要创建一个Activity容器来加载Fragment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Fragment fragment = getSupportFragmentManager().findFragmentByTag(FragmentA.class.getName());
if (fragment == null) {
fragment = FragmentA.newInstance();
getSupportFragmentManager().beginTransaction().add(R.id.activity_main,
fragment,
FragmentA.class.getName())
.commit();
}
}
}

按照代码所示,我们现在Activity中加载FragmentA 然后由FragmentA跳转到FragmentB,并且实现共享动画。

下面实现FragmentA:

FragmentA中的xml代码实现,一个ImageView 和一个Button ,其中ImageView为FragmentA的共享元素,并且为他设置属性android:transitionName="simple transition name"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout 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”
tools:context= “io.github.hexiangyuan.sharedelementtransitionsdemo.MainActivity”>
<ImageView
android:id= “@+id/imageView”
android:layout_width= “64dp”
android:layout_height= “64dp”
android:scaleType= “centerCrop”
android:transitionName= “simple transition name”
android:src= “@drawable/image” />
<Button
android:id= “@+id/btn_click”
android:layout_width= “wrap_content”
android:layout_height= “wrap_content”
android:text= “Click Me”
android:textSize= “16sp”
android:layout_below= “@+id/imageView” />
</RelativeLayout>

FragmentA的java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class FragmentA extends Fragment {
public static final String TAG = FragmentA.class.getSimpleName();
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_a, container, false);
}
public static FragmentA newInstance() {
return new FragmentA();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final ImageView imageView = (ImageView) getView().findViewById(R.id.imageView);
getActivity().findViewById(R.id.btn_click).setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
Fragment fragmentB = getFragmentManager().findFragmentByTag(TAG);
if (fragmentB == null) fragmentB = FragmentB.newInstance();
getFragmentManager()
.beginTransaction()
.addSharedElement(imageView,
ViewCompat.getTransitionName(imageView))
.addToBackStack(TAG)
.replace(R.id.activity_main, fragmentB)
.commit();
}
});
}
}

值得注意的是在addShareElement()这个方法以及addToBackStack()这个方法;

  • addShareElement:设置了作为共享元素的控件以及transitionName;
  • addToBackStack:为了让fragment回栈,如果不设置这个回栈,当跳转到fragmentB的时候,BackClick就会直接退出Activity;

那么在FragmentB就比较容易实现了:

FragmentB:

1
2
3
4
5
6
7
<ImageView
android:id= “@+id/imageView”
android:layout_width= “match_parent”
android:layout_height= “184dp”
android:scaleType= “centerCrop”
android:transitionName= “simple transition name”
android:src= “@drawable/image” />
1
2
3
4
5
6
7
8
9
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setSharedElementEnterTransition(
TransitionInflater.from(getContext())
.inflateTransition(android.R.transition.move));
}
}

只需要在FragmentB的

  • xml 的共享控件里面设置android:transitionName="simple transition name"
  • onCreate里面设置动画为android.R.transition.move

加载网络图片的元素共享(以Picasso为例)

一般在App中,我们的ImageView都是在网络URL获取的资源,那么网络加载的ImageView也是可以实现共享元素转换的,下面我们就以Picasso为例:

添加依赖

1
compile ‘com.squareup.picasso:picasso:2.5.2’

FragmentA中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Picasso.with(getActivity())
.load( “https://s3-us-west-1.amazonaws.com/powr/defaults/image-slider2.jpg”)
.fit()
.centerCrop()
.into(imageView);
getActivity().findViewById(R.id.btn_click).setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View view) {
Fragment fragmentB = getFragmentManager().findFragmentByTag(TAG);
if (fragmentB == null) fragmentB = FragmentB.newInstance();
getFragmentManager()
.beginTransaction()
.addSharedElement(imageView,
ViewCompat.getTransitionName(imageView))
.addToBackStack(TAG)
.replace(R.id.activity_main, fragmentB)
.commit();
}
});

FragmentB中添加加载图片的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ImageView imageView = (ImageView) getView().findViewById(R.id.imageView);
Picasso.with(getContext())
.load( “https://s3-us-west-1.amazonaws.com/powr/defaults/image-slider2.jpg”)
.fit()
.centerCrop()
.noFade()
.into(imageView, new Callback() {
@Override
public void onSuccess() {
startPostponedEnterTransition();
}
@Override
public void onError() {
}
});
}
  • 添加noFade禁用渐隐的效果促使动画更加的流畅。
  • 在CallBack的onSuccess()中设置startPostponedEnterTransition()

github banch

Blog

非常感谢,你能耐心读完;

共享元素转场动画Part1————Activity to Activity

Share Element Transition(共享元素变换)这一概念是在android 5.0 Material Design中提出的新的页面转场的方式。
那么本文将教你一步一步的做出炫酷的MD Style的转场动画。

什么是共享元素变换?

元素共享变换的定义:共享的View元素从一个Activity/Fragment到另一个Activity/Fragment的切换中是如何变化的;
在Google Play,Google Music 等众多Google嫡系APP中就得到了很多的运用。例如下图是Google Music的变化效果:

Google Music

上图中的Activity A 中Grid列表的CardView中ImageView和Acitivty B 的ImageView拥有共享的元素图片,于是就形成了
无缝的动画切换效果。

开始准备工作

首先,我们要开启windowContentTransitions.
如果你的targetSdk < 21,那么你需要在你的res文件夹下创建一个value-v21的资源文件夹,添加以下一行代码

1
2
3
4
5
6
<!– Base application theme. –>
<style name=“AppTheme” parent=“Theme.AppCompat.Light.DarkActionBar”>
<!– Customize your theme here. –>
//add this line to open transitions
<item name=“android:windowContentTransitions”>true </item>
</style>

Activity to Activity

然后,我们搭建两个Activity ActivityA 和ActivityB,如下图 ActivityA中有一个图片ImageView和一个按钮Button,ActivityB中有一个大图ImageView和一个描述的TextView。

ActivityA
ActivityB

ActivityA layout 布局文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout 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:paddingBottom= “@dimen/activity_vertical_margin”
android:paddingLeft= “@dimen/activity_horizontal_margin”
android:paddingRight= “@dimen/activity_horizontal_margin”
android:paddingTop= “@dimen/activity_vertical_margin”
tools:context= “io.github.hexiangyuan.sharedelementtransitionsdemo.ActivityA”>
<ImageView
android:id= “@+id/imageView”
android:layout_width= “148dp”
android:layout_height= “148dp”
android:scaleType= “centerCrop”
android:transitionName= “@string/transitions_name”
android:src= “@drawable/image” />
<Button
android:id= “@+id/button”
android:layout_width= “wrap_content”
android:layout_height= “wrap_content”
android:layout_alignParentLeft= “true”
android:layout_alignParentStart= “true”
android:layout_below= “@+id/imageView”
android:onClick= “imageClick”
android:text= “Click Me” />
</RelativeLayout>

特别注意的是android:transitionName="@string/transitions_name这一个属性,需要加在共享元素的View下,细心的朋友会发现Android Studio会变黄的tips提醒

This check finds attributes set in XML files that were introduced in a version newer than the oldest version targeted by your application (with the minSdkVersion attribute).
This is not an error;
the application will simply ignore the attribute. However, if the attribute is important to the appearance of functionality of your application, you should consider finding an alternative way to achieve the same result with only available attributes, and then you can optionally create a copy of > > the layout in a layout-vNN folder which will be used on API NN or higher where you can take advantage of the newer attribute.

意思是:这不是个错误,当你的target < 21时,你会收到lint的警告,你可以忽略或者设置不提醒。

AcitivityA java代码文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ActivityA extends AppCompatActivity {
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.acitivit_a);
imageView = (ImageView) findViewById(R.id.imageView);
}
public void imageClick(View v) {
Intent intent = new Intent( this, SimpleActivityB.class);
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(
this, imageView,
getString(R.string.transitions_name)
);
startActivity(intent, optionsCompat.toBundle());
}
}

需要留意的是

1
2
3
4
5
6
Intent intent = new Intent( this, SimpleActivityB.class);
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(
this, imageView,
getString(R.string.transitions_name)
);
startActivity(intent, optionsCompat.toBundle());

通过这个ActivityOptionsCompat.makeSceneTransitionAnimation方法,传入(context,view,transitionName)这些参数,然后跳转时候会将optiont通过bundle的方式传递过去;

然后,在ActivityB中:

layout布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout 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”
tools:context= “io.github.hexiangyuan.sharedelementtransitionsdemo.ActivityA”>
<ImageView
android:id= “@+id/imageView”
android:layout_width= “match_parent”
android:layout_height= “184dp”
android:scaleType= “centerCrop”
android:transitionName= “@string/transitions_name”
android:src= “@drawable/image” />
<TextView
android:id= “@+id/textView”
android:layout_width= “match_parent”
android:layout_height= “wrap_content”
android:layout_alignParentEnd= “true”
android:layout_alignParentRight= “true”
android:layout_below= “@+id/imageView”
android:layout_marginEnd= “16dp”
android:layout_marginRight= “16dp”
android:text= “这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容这是内容”
android:textSize= “16sp” />
</RelativeLayout>

java

1
2
3
4
5
6
7
8
public class ActivityB extends AppCompatActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
}
}

在ActivityB中就只需要在共享元素上添加一个属性
android:transitionName="@string/transitions_name"就OK了;

那么,Acitity to Activity 的共享动画就完成了,赶快Run 一下看看成果吧,是不是很简单?So easy!

finished

未完待续…….

2021年4月下旬流通领域重要生产资料市场价格变动情况

中国统计信息服务中心 卓创资讯   据对全国流通领域9大类50种重要生产资料市场价格的监测显示,2021年4月下旬与4月中旬相比,34种产品价格上涨,12种下降,4种持平。 2021年4月下旬流通领域重要生产资料市场价格变动情况  产品名称 单位 本期价格(元) 比上期 价格涨跌(元) 涨跌幅 (%) 一、黑色金属         螺纹钢(Φ16-25mm,HRB400E) 吨 5119.2 98.3 2.0 线材(Φ6.5mm,HPB300) 吨 5352.1 135.6 2.6 普通中板(20mm,Q235) 吨 5649.7 89.4 1.6 热轧普通薄板(3mm,Q235) 吨 5794.5 149.8 2.7 无缝钢管(219*6,20#) 吨 5916.3 47.0 0.8 角钢(5#) 吨 5465.2 -7.1 -0.1 二、有色金属         电解铜(1#) 吨 70406.0 3296.4 4.9 铝锭(A00) 吨 18413.4 519.6 2.9 铅锭(1#) 吨 15156.0 318.4 2.1 锌锭(0#) 吨 21762.5 131.1 0.6 三、化工产品         硫酸(98%) 吨 563.1 24.5 4.5 烧碱(液碱,32%) 吨 493.3 1.5 0.3 甲醇(优等品) 吨 2433.9 82.2 3.5 纯苯(石油苯,工业级) 吨 7291.0 506.3 7.5 苯乙烯(一级品) 吨 10151.5 791.3 8.5 聚乙烯(LLDPE,7042) 吨 8502.5 -153.4 -1.8 聚丙烯(T30S) 吨 9050.7 -160.4 -1.7 聚氯乙烯(SG5) 吨 9024.2 190.4 2.2 顺丁胶(BR9000) 吨 12098.1 -644.8 -5.1 涤纶长丝(FDY150D/96F) 吨 7600.0 -82.1 -1.1 四、石油天然气         液化天然气(LNG) 吨 3270.6 -110.9 -3.3 液化石油气(LPG) 吨 4216.4 -107.1 -2.5 汽油(95#国VI) 吨 7928.2 158.6 2.0 汽油(92#国VI) 吨 7698.9 167.2 2.2 柴油(0#国VI) 吨 6110.3 54.4 0.9 石蜡(58#半) 吨 7225.0 0.0 0.0 五、煤炭         无烟煤(洗中块) 吨 900.0 0.0 0.0 普通混煤(4500大卡) 吨 628.1 48.8 8.4 山西大混(5000大卡) 吨 713.8 49.5 7.5 山西优混(5500大卡) 吨 805.0 54.3 7.2 大同混煤(5800大卡) 吨 830.0 54.3 7.0 焦煤(主焦煤) 吨 1556.7 95.3 6.5 焦炭(二级冶金焦) 吨 2119.8 176.7 9.1 六、非金属建材         普通硅酸盐水泥(P.O 42.5袋装) 吨 479.5 4.6 1.0 普通硅酸盐水泥(P.O 42.5散装) 吨 443.9 7.9 1.8 浮法平板玻璃(4.8/5mm) 吨 2374.0 32.0 1.4 七、农产品(主要用于加工)         稻米(粳稻米) 吨 4026.8 -13.7 -0.3 小麦(国标三等) 吨 2535.4 -0.4 0.0 玉米(黄玉米二等) 吨 2803.3 25.7 0.9 棉花(皮棉,白棉三级) 吨 15988.8 399.4 2.6 生猪(外三元) 千克 22.8 -0.6 -2.6 大豆(黄豆) 吨 5216.3 -0.8 0.0 豆粕(粗蛋白含量≥43%) 吨 3516.8 147.3 4.4 花生(油料花生米) 吨 8808.3 -318.6 -3.5 八、农业生产资料         尿素(小颗料) 吨 2210.9 34.7 1.6 复合肥(硫酸钾复合肥,氮磷钾含量45%) 吨 2487.5 25.0 1.0 农药(草甘膦,95%原药) 吨 35250.0 1892.9 5.7 九、林产品         天然橡胶(标准胶SCRWF) 吨 13419.7 354.3 2.7 纸浆(漂白化学浆) 吨 5967.5 -93.9 -1.5 瓦楞纸(高强) 吨 3813.8 -144.9 -3.7 注:上期为2021年4月中旬。    附注   1.指标解释   流通领域重要生产资料市场价格,是指重要生产资料经营企业的批发和销售价格。与出厂价格不同,生产资料市场价格既包含出厂价格,也包含有经营企业的流通费用、利润和税费等。出厂价格与市场价格互相影响,存在时滞,两者的变动趋势在某一时间段内有可能会出现不完全一致的情况。   2.监测内容   流通领域重要生产资料市场价格监测内容包括9大类50种产品的价格。类别与产品规格说明详见附表。   3.监测范围   监测范围涵盖全国31个省(区、市)300多个交易市场的近2000家批发商、代理商、经销商等经营企业。   4.监测方法   价格监测方法包括信息员现场采价,电话、即时通讯工具和电子邮件询价等。   5.涨跌个数的统计   产品价格上涨、下降、持平个数按照涨跌幅(%)进行统计。   6.发布日期   每月4日、14日、24日发布上一旬数据,节假日顺延。 附表:流通领域重要生产资料市场价格监测产品规格说明表  序号 监测产品 规格型号 说明   一、黑色金属      1   螺纹钢 Φ16-25mm,HRB400E 屈服强度≥400MPa  2 线材 Φ6.5mm,HPB300 屈服强度≥300MPa  3 普通中板 20mm,Q235 屈服强度≥235MPa  4 热轧普通薄板 3mm,Q235 屈服强度≥235MPa  5 无缝钢管 219*6,20# 20#钢材,屈服强度≥245MPa  6 角钢 5# 屈服强度≥235MPa   二、有色金属      7 电解铜 1# 铜与银质量分数≥99.95%  8 铝锭 A00 铝质量分数≥99.7%  9 铅锭 1# 铅质量分数≥99.994% 10 锌锭 0# 锌质量分数≥99.995%   三、化工产品     11  硫酸 98% H2SO4质量分数≥98% 12 烧碱(液碱) 32% NaOH质量分数≥32%的离子膜碱 13 甲醇 优等品 水质量含量≤0.10% 14 纯苯(石油苯) 工业级 苯纯度≥99.8% 15 苯乙烯 一级品 纯度≥99.5% 16 聚乙烯(LLDPE) 7042 熔指:2.0±0.5g/10min 17 聚丙烯 T30S 熔指:3.0±0.9g/10min 18 聚氯乙烯 SG5 K值:66-68 19 顺丁胶 BR9000 块状、乳白色,灰分≤0.20% 20 涤纶长丝 FDY150D/96F 150旦,AA级   四、石油天然气     21 液化天然气 LNG 甲烷含量≥75%,密度≥430kg/m3 22 液化石油气 LPG 饱和蒸汽压1380-1430kPa 23 汽油 95#国VI 国VI标准 24 汽油 92#国VI 国VI标准 25 柴油 0#国VI 国VI标准 26 石蜡 58#半 熔点不低于58℃   五、煤炭     27 无烟煤 洗中块 挥发分≤8% 28 普通混煤 4500大卡 山西粉煤与块煤的混合煤,热值4500大卡 29 山西大混 5000大卡 质量较好的混煤,热值5000大卡 30 山西优混 5500大卡 优质的混煤,热值5500大卡 31 大同混煤 5800大卡 大同产混煤,热值5800大卡 32 焦煤  主焦煤 含硫量<1% 33 焦炭 二级冶金焦 12.01%≤灰分≤13.50%   六、非金属建材     34 普通硅酸盐水泥 P.O 42.5袋装 抗压强度42.5MPa 35 普通硅酸盐水泥 P.O 42.5散装 抗压强度42.5MPa 36 浮法平板玻璃 4.8/5mm 厚度为4.8/5mm的无色透明玻璃   七、农产品(主要用于加工)     37 稻米 粳稻米 杂质≤0.25%,水分≤15.5% 38 小麦 国标三等 杂质≤1.0%,水分≤12.5% 39 玉米 黄玉米二等 杂质≤1.0%,水分≤14.0% 40 棉花(皮棉) 白棉三级 纤维长度≥28mm,白或乳白色 41 生猪 外三元 三种外国猪杂交的肉食猪 42 大豆 黄豆 杂质≤1.0%,水分≤13.0% 43 豆粕 粗蛋白含量≥43% 粗蛋白≥43%,水分≤13.0% 44 花生 油料花生米 杂质≤1.0%,水分≤9.0%   八、农业生产资料     45 尿素 小颗料 总氮≥46%,水分≤1.0% 46 复合肥 硫酸钾复合肥 氮磷钾含量45% 47 农药(草甘膦) 95%原药 草甘膦质量分数≥95%   九、林产品     48 天然橡胶 标准胶SCRWF 杂质含量≤0.05%,灰分≤0.5% 49 纸浆 漂白化学浆 亮度≥80%,黏度≥600cm³/g 50 瓦楞纸 高强 80-160g/m2  

盛来运:全面客观看待当前经济形势

全面客观看待当前经济形势
——访国家统计局副局长盛来运   如何全面客观看待一季度中国经济表现?制造业投资两年平均增速依然为负原因何在?碳达峰、碳中和是否会对经济增速产生影响?围绕热点问题,经济日报与中国经济网记者日前采访了国家统计局副局长盛来运。   中国经济仍处在稳定恢复之中   记者:一季度中国经济同比增长18.3%,两年平均增长5.0%。一季度经济的含金量如何?中国经济是否已完全恢复常态运行?   盛来运:今年一季度,国内外环境依然复杂严峻,在以*同志为核心的党中央坚强领导下,各地区各部门认真贯彻落实党中央、国务院决策部署,科学组织疫情防控和经济社会发展,经济持续稳定恢复,开局良好。一季度我国经济同比增长18.3%,增速在主要经济体中名列前茅。同时,就业形势总体稳定,民生继续改善,经济发展的质量和效益持续提升,可以说一季度经济增长的含金量比较高、成色比较足。   客观看待经济形势,需要从多个视角分析比较,既看同比增速,又看环比增速;既看宏观总体数据,也看中观行业变化情况,还要看市场主体的表现和感受。   从同比来看,去年一季度我国经济增速为-6.8%,导致基数比较低。为初步消除同比基数的影响,我们以2019年相应同期数为基数,采用几何平均方法计算出两年平均增速为5%,这一增速就平稳得多。从环比来看,与去年四季度相比,一季度0.6%的环比增速说明当前经济恢复边际放缓。   *近很多专家在讨论经济是否恢复了常态,对此可以有3个参照标准:一是*对水平,二是平均增长速度,三是潜在增长率。从*对水平看,去年前三季度总量基本恢复到疫前水平,但从平均增速和潜在增长率来讲,还有一定距离。初步测算,我国现阶段潜在增长率在5.8%左右,有专家更乐观的估计是6%或更高一点。从这个角度看,5%的两年平均增速不仅低于2016年至2019年平均增速,而且距离应达到的潜在增长率还有一定缺口,说明当前经济仍处在恢复过程中,不能说完全恢复了正常状态。   从中观看,行业恢复不均衡。比如工业恢复相对较好,服务业受部分地区疫情反复影响较大。一季度服务业两年平均增长4.7%,而过去几年的平均增速都在6%以上。此外,微观主体的差异也很大,尤其是小微企业、个体工商户去年以来受疫情冲击更大一些。原材料价格成本在上涨,融资难融资贵的问题还没有根本改观,实体经济仍处在恢复之中。从这些情况看,当前经济恢复的基础还不牢固,要按照党中央、国务院决策部署,继续扎实推进“六稳”“六保”工作,做好对企业的纾困帮扶,保持宏观政策连续性稳定性可持续性,巩固经济复苏的基础。   “前高后低”不代表经济走弱   记者:随着去年同期低基数的影响越来越小,今年的GDP增速是否会逐季下降?   盛来运:去年一至四季度我国GDP增速分别为-6.8%、3.2%、4.9%和6.5%,根据去年“前低后高”的事实,今年的同比增速大概率是“前高后低”。   同比增速由高到低,不能得出中国经济持续走弱的判断。要结合环比、两年平均等视角综合研判。实质上,从环比增速、结构优化、新动能成长、经济发展质量效益的一些指标来看,中国经济有条件有潜力保持持续稳定健康发展。比如从先行指标看,3月份制造业采购经理指数为51.9%,创近期新高,4月份保持51.1%的相对高位,表明中国经济稳定恢复势头良好。   记者:今年一季度GDP环比增长0.6%,与往年平均水平相比,表现如何?   盛来运:过去5年,除去年一季度受特殊因素影响外,2016年至2019年一季度GDP环比增速均在1%以上,均值为1.8%,今年一季度0.6%的环比增速确实低于往年平均水平。但其中有一些特殊原因,一个原因就是去年四季度经济恢复较快,基数较大。而今年1月份至2月份局部地区出现散发性疫情反弹,给服务业尤其是接触性服务业带来较大影响,客观上影响了环比增速。此外,从边际效应递减规律的角度来看,当一个经济体的发展接近潜在增长速度时,增量的变化是边际递减的。目前,我国经济增长正向潜在增长率逼近,边际放缓也是正常表现。   记者:实现碳达峰、碳中和离不开经济发展方式转型升级,这是否会影响中国经济增速?   盛来运:对于我国这样一个发展中国家来说,工业化进程尚未完成,人均GDP刚刚突破1万美元,一方面要实现现代化,另一方面要实现碳达峰、碳中和,压力和挑战前所未有。   但是,碳达峰、碳中和也将创造新发展机遇,催生新产业新业态,并推动技术创新和生产方式、消费方式转型。只要我们抓住机遇积*应对,中国经济会攻坚克难,实现更健康更可持续的发展。   对消费和制造业投资恢复有信心   记者:一季度,社会消费品零售总额同比增长33.9%,两年平均增长4.2%,这是否意味着目前消费已恢复元气?   盛来运:同比高增速主要受去年低基数影响,如果消除基数影响,一季度社会消费品零售总额两年平均增速只有4.2%,低于过去几年常态化的平均增速,大概有两个点以上的差距。这说明,当前服务业和消费受疫情的影响还比较大,恢复至常态还需要继续努力。   不过,从3月份数据来看,消费正在边际好转。3月份社会消费品零售总额同比增长34.2%,比1月份至2月份提升0.4个百分点,这是一个积*变化。此外,3月份社会消费品零售总额环比增速为1.75%,显示出消费继续改善,也反映出我国消费市场比较广阔,潜力比较大,弹性比较足。随着疫情好转和消费环境改善,消费潜力将进一步释放。   记者:一季度制造业投资两年平均下降2.0%,而房地产开发投资两年平均增长7.6%,如何看待这“一冷一热”?有人担心可能重回“房地产拉动经济”的老路,对此您怎么看?   盛来运:这个问题确实需要高度关注。今年一季度制造业投资总量比2019年同期低3.9%,两年平均下降2.0%,还没有恢复到2019年*对量水平。制造业投资是实体经济供给能力形成的基础和先行指标,说明当前我国制造业在恢复过程中还存在一些困难。   影响制造业投资的因素主要有两方面。一是外部环境复杂多变,全球疫情仍在蔓延,供应链、产业链受到很大冲击,部分企业家尤其是民营企业家观望气氛较浓。二是与企业的资金能力有一定关系。面对疫情,对企业来说保生存是*位的。去年三季度以前,企业利润是负增长,四季度才开始转正,一定程度上影响了企业的自主投资能力。但是,这两个方面都在好转,企业效益已经出现积*变化。   目前有关各方都在加大对制造业的支持力度,出台了一系列支持实体经济发展的政策措施。相信随着疫情有效好转和政策持续发力,制造业投资会有积*变化。   关于对“房地产拉动经济”的担忧,我认为不能从表面数据得出这个结论,而要从实际动力结构变化来分析,到底是什么拉动了一季度中国经济增长?从“三驾马车”的贡献来看,今年一季度18.3%的GDP增速中,有11.6个百分点由消费贡献,贡献率达到63%;投资转化成资本形成拉动4.6个百分点,贡献率是24%;货物和服务净出口的贡献率占2.2个百分点,贡献率是13%。可以看出,一季度经济增长是消费、投资、进出口共同拉动的,我国经济增长主要靠内需,尤其是消费的“压舱石”作用明显。因此,我国经济增长不会回到投资拉动的老路上去。   全年物价将温和上涨   记者:一季度CPI同比持平,但PPI却快速上涨,近期大宗商品价格上涨明显,如何看待今年的通胀压力?   盛来运:当前,我国市场价格走势出现了明显分化。一方面,CPI仍处低位,一季度同比持平,3月份由负转正增长0.4%;另一方面,3月份PPI同比增长4.4%,比上个月跳升了2.7个百分点,令市场担忧通胀是否会加速到来。   目前国际通胀水平确实有所抬升,主要原因有几方面,一是今年以来世界经济总体在复苏,虽然疫情仍在全球范围内蔓延,但一些发达国家疫情得到明显缓解,疫苗加快接种,增强了国际社会共抗疫情推动发展的信心。二是在总需求拓展的同时,生产端供应跟不上需求扩张的步伐,尤其是一些原材料生产国和供应国还没有及时复工复产,供需缺口是价格上涨的基础。三是一些国家扩张性的量化宽松政策带来外溢效应,催生了世界范围内价格上涨的预期和趋向。此外,逆全球化思潮抬头、保护主义盛行、技术封锁等都加大了企业的生产成本和消费者的负担。   对我国来讲,尽管来自外部的输入性通胀压力有所加大,但通胀水平总体处在可控状态。首先,目前我国物价指数的平均水平相对较低,3月份扣除食品和能源价格的核心CPI上涨0.3%。第二,我国经济基本面和政策面不支持出现大规模的价格上涨。从基本面看,目前我国生产扩张的幅度快于需求,工业恢复快于服务业,供大于求的现实使物价上涨缺乏基本面的支持。从政策面看,我国实行积*的财政政策和稳健的货币政策,没有搞“大水漫灌”,而是通过精准调控支持薄弱环节,通过“六稳”“六保”促进经济稳定恢复。第三,从结构性因素看,目前我国生猪存栏量已基本恢复至常年状态的90%以上,生猪的生产供应是有保障的,一定程度上对冲了通胀压力。第四,从传导机制看,上游工业品价格上涨对下游产品价格的传导效应确实存在,但是逐渐递减。我国工业门类比较齐全,产业链相对较长,下游产品竞争比较充分,很多产品处于供过于求的状态,影响价格大幅上涨的因素会受到一定抑制。 从后期走势来看,由于基数、结构性因素及输入性因素的影响,我国物价将呈现温和上涨的状态,但总体可控,全年CPI涨幅将明显低于3%左右的预期目标。 (经济日报记者 熊丽;中国经济网记者 马常艳) (原文链接 https://proapi.jingjiribao.cn/readnews.html?id=248859)

PO模式简介与示例

PO模式简介与示例

文章目录
简介
优点
基本原则
方法意义
字段意义
用例分层
demo
原始测试脚本
PO模式
testcase
page
简介
PO模式(Page Object Model)是自动化测试项目开发实践的*佳设计模式之一。

它的主要用途是把一个具体的页面转换成编程语言当中的一个对象,页面特性转化成对象属性,页面操作转换成对象方法。

在自动化测试当中,主要用来实现对页面操作和测试,逻辑的一个分离,PO思想*开始来源于马丁富勒(marktin Flewer)在2004年发表的一篇文章。*初是叫作Window driver,后来selenium沿用这种思想,后来就改成了POM。

PO模式的核心思想是通过对界面元素的封装减少冗余代码,同时在后期维护中,若元素定位发生变化,只需要调整页面元素封装的代码,提高测试用例的可维护性、可读性。

优点
代码可读性高,减少冗余代码;
增加用例的可维护性,业务代码和测试代码被分开,降低耦合性维护成本低;
增强复用性。
基本原则
方法意义
用公共方法来代表UI所提供的功能
如:首页的搜索功能,名站功能
方法应该返回其他的page object或者返回用于断言的数据
如,首页点击搜索框进入搜索页面
相同的行为不同的结果,可以建模不同的方法
如:登录行为可以拆分成登录成功(进入首页),或者登录失败(仍然停留在登录页面)
不会在PO内创建断言
字段意义
不要暴露页面内的元素给外部
如:页面内的按钮的id不能被其他的case调用(变化的元素都控制在PO内部)
不需要建模整个页面元素
如:每个页面的元素不需要全部列出来,用到什么写什么
用例分层
基于 POM 的用例组织结构,良好的分层机制,让不同层去做不同类型的事情,让代码结构清晰,增加复用性。

一般而言,可以分以下几层:

page:完成对页面的封装
testcase:调用各类 page 完成业务流程并进行断言
driver:完成对 Web、Android、iOS、接口的驱动
data:配置文件和数据驱动
utils:其他便捷的功能封装,可选
demo
测试demo:雪球app
操作步骤:打开首页 –> 点击搜索框 –> 输入股票代码 –> 断言股票价格是否大于1快

原始测试脚本
from appium import webdriver

class TestDemo:
def setup(self):
caps = {
‘platformName’: ‘Android’,
‘appPackage’: ‘com.xueqiu.android’,
‘appActivity’: ‘.view.WelcomeActivityAlias’,
‘noReset’: True,
‘skipServerInstallation’: True
}

self.driver = webdriver.Remote(‘http://127.0.0.1:4723/wd/hub’, caps)
self.driver.implicitly_wait(10)

def test_search(self):
“””
原脚本
“””
self.driver.find_element_by_id(“com.xueqiu.android:id/home_search”).click() # 点击首页搜索框
self.driver.find_element_by_id(“search_input_text”).send_keys(‘二三四五’) # 输入股票代码
self.driver.find_element_by_id(“name”).click() # 点输入联想列表的股票名称

price = self.driver.find_element_by_id(“current_price”) # 获取股票价格

assert float(price.text) > 1.0 # 断言是否大于1元

def teardown(self):
self.driver.quit()

PO模式
编写用例的顺序

根据界面封装page类与方法()
编写用例,不断重构明确 page 里方法的入参和返回值
开始实现 page 内的方法
把这个页面需要的一些核心操作封装成方法
在有页面发生跳转的情况下,发生跳转的方法需要返回跳转页面的类对象
testcase
from page.app import App

class TestDemo:
def setup(self):
self.search_page = App.start().to_search()

def test_search_po(self):
self.search_page.search(‘二三四五’)
assert self.search_page.get_current_price() > 1.0

def teardown(self):
App.quit()

page
app.py
from appium import webdriver
from selenium.webdriver.remote.webdriver import WebDriver

from page.main_page import MainPage

class App:
driver: WebDriver = None

@classmethod
def start(cls):
“””
启动app:后进入首页
:return: 首页
“””
caps = {
‘platformName’: ‘Android’,
‘appPackage’: ‘com.xueqiu.android’,
‘appActivity’: ‘.view.WelcomeActivityAlias’,
‘noReset’: True,
‘skipServerInstallation’: True
}

cls.driver = webdriver.Remote(‘http://127.0.0.1:4723/wd/hub’, caps)
cls.driver.implicitly_wait(10)

return MainPage(cls.driver)

@classmethod
def quit(cls):
cls.driver.quit()

main_page.py
from selenium.webdriver.remote.webdriver import WebDriver

from page.search_page import SearchPage

class MainPage(object):
def __init__(self, driver: WebDriver):
self.driver = driver

def to_search(self):
self.driver.find_element_by_id(“com.xueqiu.android:id/home_search”).click() # 点击首页搜索框
return SearchPage(self.driver)

search_page.py
from selenium.webdriver.remote.webdriver import WebDriver

class SearchPage(object):
def __init__(self, driver: WebDriver):
self.driver = driver

def search(self, keyword):
self.driver.find_element_by_id(“search_input_text”).send_keys(keyword) # 输入股票代码
self.driver.find_element_by_id(“name”).click() # 点输入联想列表的股票名称
return self

def get_current_price(self):
return float(self.driver.find_element_by_id(“current_price”).text)

python的这四大函数类型得牢记(变量,引用,匿名,递归)

python的这四大函数类型得牢记(变量,引用,匿名,递归)

四大函数类型
一. 局部和全局变量
二. 引用
三. 匿名函数
四. 递归

前言
作者:神的孩子都在跳舞

关注我的csdn博客,更多python知识还在更新

一. 局部和全局变量
局部变量
(1)含义:就是在函数内部定义的变量(作用域仅仅局限在函数的内部)不同的函数可以定义相同的局部变量,但是各自用各自的 不会产生影响
(2)作用:为了临时的保存数据 需要在函数中定义来进行存储
全局变量:可以被所有函数调用
突发情况
1.当全局变量和局部变量出现重复定义的时候,程序会优先执行使用函数内部定义的变量(地头蛇)
2. 如果在函数的内部要想对全局变量进行修改的话 必须使用global 关键字进行声明
3. 对于可变类型(dict、list)来讲,全局变量要想在函数中修改的话,我们不需要用global关键字去声明的(因为对象的内存地址不会改变)

代码演示
c=3#c为全局变量,可以被所有函数调用
a=2
def Test():
b=2#b为局部变量,只能在函数内被调用
global a#局部变量变为全局变量
a=4
print(‘{}’.format(a))
pass
def Test2():
c=4
print(‘{}’.format(c))
pass
Test2()
print(c)#c没有发生改变说明全局变量在函数中无法修改
Test()
print(a)#a发生了改变说明声明全局变量 后才可以修改

# 可变类型列表,字典 dict list,可以在函数中修改
listA=[]
print(type(listA))
def demoList():
listA.append(‘python’)#向空列表里面添加数据
pass
demoList()
print(listA)#发现输出数据了

# 不可变类型str tuple number【int、float、complex】

二. 引用
在python中,值是靠引用来传递来的,可以用id()查看一个对象的引用是否相同,id是值保存在内存中那块内存地址的标识。

可变类型列表和字典:函数调用全局变量后地址不会变化

代码演示

li=[]
print(‘原来的地址{}’.format(id(li)))
def Test(n):
li.append([1,3,’chen’])
print(id(n))
print(‘内部的{}’.format(n))
pass
print(id(li))
Test(li)#输出我们发现地址是一样的
print(‘外部的变量对象{}’.format(li))

#不可变类型
a=1
def func(x):
print(‘x刚开始的地址{}’.format(id(x)))
x=2
print(‘x的值修改之后地址{}’.format(id(x)))#改变类型后地址发生了变化
print(x)
pass
#调用函数
print(‘a的地址:{}’.format(id(a)))
func(a)
print(a)

三. 匿名函数
介绍:使用lambde关键字去创建函数
没有名字的函数

匿名函数冒号后面的表达式有且只有一个
注意:是表达式,而不是语句

自带return,而这个return的结果就是表达式计算后的结果
缺点:lambde只能是单个表达式,不是一个代码块,lambde的设计就是为了满足简单函数的场景
仅仅能封装有限的逻辑,复杂逻辑实现不了,必须使用def来处理

代码演示
M=lambda x,y:x+y
print(M(2,3))#M相当于一个调用函数
#可以使用if-else语句,三元运算

Test=lambda x,y:x if x>y else y#意思就是调用x和y两个变量,如果x大于y就取x否则取y
print(Test(2,3))

# 也可以单个参数使用
Test=lambda x:(x**2)
print(Test(10))

四. 递归
简单理解:自己调用自己
必须有一个明确的结束条件
优点:逻辑简单、定义简单
缺点:容易导致栈溢出,内存资源紧张,甚至内存泄漏
下列代码是求阶乘:以循环的方式去实现
def jiecheng(n):
result=1
for item in range(1,n+1):# range左闭右开,所以要加一
result*=item
pass
return result
print(jiecheng(5))
# 递归的方式实现
def factorial (n):
if n == 1:
return 1
return n * factorial (n-1)
print(factorial(5))

软件测试系统学习流程和常见面试题

软件测试系统学习流程和常见面试题

 

学习流程
一、必备技能
1、编程基础,能看懂前端页面,掌握一门语言:php/python/java等

2、数据库知识,建议准备好sql语言,能掌握高级查询使用基本可以应对了。

3、软件测试理论,这个大家都不陌生,也是必考的了,应该可以轻松应付。要注意准备下web和app测试和性能测试这块,现在做web、app和微信小程序的公司好多。

4、根据公司具体的职位要求可以准备的有linux的命令,CMMI的基础知识,TCP/IP的基础知识等。

二、学习流程
1.功能测试学习

功能是软件测试*基础*本质的工作,就和地基一样,所以功能测试基础一定要打好。在了解基础的功能测试的时候可以看一看《软件测试的艺术》这本书对才学习软件测试的人真的很有用。

2.接口测试

接口是前后端数据交互的通道,接口测试也是测试中很重要的一部分,接口测试的学习包括测试工具的学习:apipost、jmeter、loadrunner等。通信协议,http协议也需要重点学习,推荐看一下《图解网络-小林coding》,可以帮助我们基础了解互联网通信,前端如何传递数据到后端。

3.性能测试

性能测试一般是接口测试的一部分,通过接口对服务器进行的测试,查看服务器各种数值,性能测试包括很多分类测试注重点也不一样,如:压力测试、负载测试、稳定姓测试等。

4.自动化测试

自动化测试可以分为:接口自动化测试和ui自动化测试。

学习自动化测试,需要掌握一门语言,php/python/java等。接口自动化测试要掌握requests框架,ui自动化测试需要掌握:web端需要掌握selenium、app端需要掌握appium。

5.Linux操作系统

学习Linux操作系统是为了自己部署测试环境,方便于自己测试。学习好Linux shell语句也很重要,学习书籍可以看看《鸟哥的linux私房菜》。

6.前端、后端和数据库语言

能够看懂前端页面,知道前端页面是如何编写出来的,ajax传值的方法,后端会一门后端语言,php/python/java等。会数据库基本的sql语句的编写。

常见面试题
一、功能测试

1.说说你以前公司的测试流程。必答题。主要结合自己的项目经验相信讲一个自己做过的项目,从立项到测试结束,当然侧重测试和自己所做的内容。这里面试官一般都会根据你说的再提问。

2.功能测试主要有那些测试方法?

等价类划分、边界值、因果图划分、正交、场景、随机、错误推断、测试大纲

A:等价类划分法: : 1:有效等价类: 2:无效等价类:

案例:比如一个登陆输入框,规定只能输入中文,同时长度为6-10,

通过等价类设计测试用例:

测试用例中重要的三步: 输入 操作 预计结果 如果与预期结果不符合就是bug

有效等价类: 输入:输入长度为6的中文,输入的为王小明,这就是有效等价类

无效等价类:

1: 输入长度为4的中文,输入位小名,点击登录,预计结果长度不符合要求

2: 输入长度为6,但是是英文的,点击登录,预计结果 请输入中文

3: 输入长度为4,而且不是中文的,是数字,1234,点击登录,预计结果请输入中文并且长度为6-10位

4:输入长度为12而且不是中文的,比如qwertyuiopas,点击登录,预计结果请输入中文并且长度为6-10位

B:边界值法:

应用场景:边界值往往和等价类划分法一起使用,形成一套更为完善的测试方案,找到有效数据和无效数据的分界点,

注解边界值一般和有效等价类划分法配合使用:

案例:比如一个登陆输入框,规定只能输入中文,同时长度为6-10,

上面输入框的边界的:如果固定大于等于6,并且小于等于10,

那左边界就是 5和 6

右边界是:10 和 11

测试用例:

1:输入的为王小明,这就是有效等价类和边界值的结合使用

2:输入小名,这就是边界值为5,同时有效等价类

3:输入欧阳致远家,这就是边界值10,同时等价类有效

4:输入欧阳致远啦啦,这就是边界值为11,同时有效等价类

C:因果图及判定表法:

应用场景:在一个界面中有多个控件,如果控件之间有组合关系或者限制关系,不同的控件组合会产生不同的输入结果,为了弄清楚不同的输入组合会产生咋样的输出结果,可以使用因果图及判定表法:

判断是儿童还是青年还是成年人:

条件1:年龄 age

条件2:身高height

条件3:体重weight

输入年龄5,体重80公斤,身高170,查无此人

输入提高80,身高170,输入年龄20,成年人

输入年龄5,体重30,身高60,小孩

D:正交表:

应用场景:在一个界面中有多个控件,每个控件有多个取值,测试时考虑不同的控件不同取值之间的多种组合,但组合数量巨大(>20种,20种以下一般考虑判定表因果图),没有必要全部测试,如何从所有的组合中挑选*少、*优的组合进行测试,可以使用正交排列法。

正交表的测试思想特点:

1)使用每个控件的每个取值参与组合的次数是基本相等的(均匀的)

2)在所有的组合数据中,选取数据时,应该均匀的选取,而不能从局部选取。

3)如果时间允许,尽可能的多测一些组合

正交表:主要针对一个输入框里面可能有多个值,而且数量巨大

年龄 体重 省 市 县

比如:输入年龄 18,体重45,山西 大同 阳高

E:测试大纲法

适用场合:程序包含多个窗口,每个窗口中又有多个功能,这些功能之间又有一定的联系。为了梳理清楚窗口之间以及窗口不同功能之间的联系,使用测试大纲法。

F:场景法

适用场合:大多数的业务比较复杂的软件系统都适合使用场景法(便于将各个功能点串起来,便于形成完整的业务感觉)是一种基于软件业务的测试方法,把自己当成*终用户,尽可能的模拟用户在使用此软件的操作

案例:

场景一:比如买东西:输入袜子,点击查询,出现列表,点击七匹狼,点击进入详情,点击加入购物车,点击去购物车结算,点击收获地址,点击支付,支付成功

场景二:比如买东西:输入袜子,点击查询,出现列表,点击七匹狼,点击进入详情,点击加入购物车,点击去购物车结算,点击收获地址,点击取消支付

G: 错误推断法

基于经验和直觉推测程序中所有可能存在的各种错误 , 从而有针对性的设 计测试用例的方法

在进行灰盒测试的时候经常用到此方法

H:随机测试

随意测试,不考虑任何用例和需求,完全站在一个用户或者的角度对产品进行使用。

适用场景:

1) 所有之前设定的用例已经 执行完毕

2)海量的条件组合无法一遍 历的时候

二.性能测试和接口测试
1.一般使用什么性能测试和接口测试工具

性能测试:jmeter、loadrunner

接口测试:apipost、postman
ApiPost – 可直接生成文档的API调试、管理工具​

2.如何进行接口测试,接口测试需要注意那些方面

通过apipost、postman等工具,根据接口文档,输入请求方法get、post等方法,输入url,输入需要传递的参数,然后查看响应是否符合接口文档所显示的。需要注意json响应中的参数的类型等

3.性能测试具体指什么

指验证软件的性能可以满足系统规格给定的指定要求的性能指标。性能测试是一个比较大的范围,可以进一步衍生出负载测试、强度测试、压力测试、稳定性测试。通过自动化测试工具模拟多种正常、异常、峰值条件,对系统各项性能指标测试

4.什么是压力测试

压力测试方法测试系统在一定饱和状态下,例如cpu、内存在饱和使用情况下,系统能够处理的会话能力,以及系统是否会出现错误。测试出系统所能承受的*大*限。是指系统在*限下的压力情况,系统在什么样的压力下会导致系统得到失效,无法正常运行。100个用户连续访问1小时可以看做是压力测试,连续访问10小时可以认为是负载测试

三.自动化测试
1.自动化测试指什么

ui界面自动化和接口自动化

ui界面自动化:通过代码模拟人对程序进行的操作

接口自动化;通过代码实现大批量接口测试

2.自动化测试一般使用什么工具

web测试:python+selenium+浏览器驱动

app测试:python+appium

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速