Hexo


  • Startseite

  • Archiv

Unbenannt

Veröffentlicht am 2017-08-11

##探究fragment转场动画的执行流程

###我们都知道fragment可以设置它的转场动画,来实现fragment之间的切换的动画效果。如下代码:

1
2
3
4
5
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.menushow,R.anim.menuhide);
fragmentTransaction.add(R.id.ad_container,new ClsInfoFragment(),"tag1");
fragmentTransaction.commitAllowingStateLoss();

这样简单几句话就可以实现fragment之间的切换效果动画,那其中都经历了什么,我们来跟踪源码看一看流程是怎么走的。

我们先从setCustomAnimations(int enter, int exit)走起,进入源码可见这是一个抽象方法,属于抽象类FragmentTransaction,需要注意的是,此处的exit动画并不适用于出栈动画,如果你试图在这使用它做一个出栈动画是无效的,出栈动画需要用另一个方法
setCustomAnimations(enter, exit, popEnter, popExit),第四个参数才是设置出栈动画

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
33
34
35
36
37
    /**
* Set specific animation resources to run for the fragments that are
* entering and exiting in this transaction. These animations will not be
* played when popping the back stack.
*
* @param enter An animation or animator resource ID used for the enter animation on the
* view of the fragment being added or attached.
* @param exit An animation or animator resource ID used for the exit animation on the
* view of the fragment being removed or detached.
*/
public abstract FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int enter,
@AnimatorRes @AnimRes int exit);



两个方法的对比

/**
* Set specific animation resources to run for the fragments that are
* entering and exiting in this transaction. The <code>popEnter</code>
* and <code>popExit</code> animations will be played for enter/exit
* operations specifically when popping the back stack.
*
* @param enter An animation or animator resource ID used for the enter animation on the
* view of the fragment being added or attached.
* @param exit An animation or animator resource ID used for the exit animation on the
* view of the fragment being removed or detached.
* @param popEnter An animation or animator resource ID used for the enter animation on the
* view of the fragment being readded or reattached caused by
* {@link FragmentManager#popBackStack()} or similar methods.
* @param popExit An animation or animator resource ID used for the enter animation on the
* view of the fragment being removed or detached caused by
* {@link FragmentManager#popBackStack()} or similar methods.
*/
public abstract FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int enter,
@AnimatorRes @AnimRes int exit, @AnimatorRes @AnimRes int popEnter,
@AnimatorRes @AnimRes int popExit);

因为FragmentTransaction是个抽象类,那么我们找到他的实现类BackStackRecord,位于android.support.v4.app包下。它的setCustomAnimations方法只是简单的将我们传入的参数赋值给自身的变量,并返回自身对象

1
2
3
4
5
6
7
8
9
@Override
public FragmentTransaction setCustomAnimations(int enter, int exit,
int popEnter, int popExit) {
mEnterAnim = enter;
mExitAnim = exit;
mPopEnterAnim = popEnter;
mPopExitAnim = popExit;
return this;
}

那我们再来看下一步fragmentTransaction.add(R.id.ad_container,new ClsInfoFragment(),”tag1”)都做了什么。依然还是BackStackRecord类。

1
2
3
4
5
 @Override
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}

BackStackRecord里的add方法里调用了一个doAddOp方法,源码如下:

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
33
34
35
36
 private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
final Class fragmentClass = fragment.getClass();
final int modifiers = fragmentClass.getModifiers();
if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
|| (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
+ " must be a public static class to be properly recreated from"
+ " instance state.");
}

fragment.mFragmentManager = mManager;

if (tag != null) {
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
throw new IllegalStateException("Can't change tag of fragment "
+ fragment + ": was " + fragment.mTag
+ " now " + tag);
}
fragment.mTag = tag;
}

if (containerViewId != 0) {
if (containerViewId == View.NO_ID) {
throw new IllegalArgumentException("Can't add fragment "
+ fragment + " with tag " + tag + " to container view with no id");
}
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
throw new IllegalStateException("Can't change container ID of fragment "
+ fragment + ": was " + fragment.mFragmentId
+ " now " + containerViewId);
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
}

addOp(new Op(opcmd, fragment));
}

这里先是将fragment关联到fragmentmanager对象,为fragment设置tag,然后调用一个addOp(new Op(opcmd, fragment))方法,opcmd是上面传入的一个OP_ADD常量,在BackStackRecord中有一系列常量如OP_HIDE,OP_SHOW,OP_DETACH,OP_ATTACH,OP_ADD等等来控制下面的流程走向。让我们进入addOp方法:

1
2
3
4
5
6
7
 void addOp(Op op) {
mOps.add(op);
op.enterAnim = mEnterAnim;
op.exitAnim = mExitAnim;
op.popEnterAnim = mPopEnterAnim;
op.popExitAnim = mPopExitAnim;
}

在这里就将setCustomAnimations方法传入的值给一个Op对象进行赋值并将它添加到一个list里备用。Op是定义在BackStackRecord类里的一个静态final类,BackStackRecord里有一个ArrayList管理这些对应的Op对象。

我们再来看下一步fragmentTransaction.commitAllowingStateLoss()

1
2
3
4
@Override
public int commitAllowingStateLoss() {
return commitInternal(true);
}

跟踪到commitInternal方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int commitInternal(boolean allowStateLoss) {
if (mCommitted) throw new IllegalStateException("commit already called");
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(TAG);
PrintWriter pw = new PrintWriter(logw);
dump(" ", null, pw, null);
pw.close();
}
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}

Fragemntmanager也是个抽象类,它的实现为FragmentManagerImpl类,进入源码跟踪可知
这个方法调用了fragemntmanager的enqueueAction(OpGenerator action, boolean allowStateLoss)方法,它的实现在FragmentManagerImpl 的 enqueueAction方法

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
/**
* Adds an action to the queue of pending actions.
*
* @param action the action to add
* @param allowStateLoss whether to allow loss of state information
* @throws IllegalStateException if the activity has been destroyed
*/
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mDestroyed || mHost == null) {
if (allowStateLoss) {
// This FragmentManager isn't attached, so drop the entire transaction.
return;
}
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
mPendingActions = new ArrayList<>();
}
mPendingActions.add(action);
scheduleCommit();
}
}

我们可以看到这方法中执行了一个scheduleCommit()方法,继续往下走进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Schedules the execution when one hasn't been scheduled already. This should happen
* the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when
* a postponed transaction has been started with
* {@link Fragment#startPostponedEnterTransition()}
*/
private void scheduleCommit() {
synchronized (this) {
boolean postponeReady =
mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
if (postponeReady || pendingReady) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
}

我们可以看到这个方法里先remove掉主线程handler里已经存在的退出的动画任务 mExecCommit,
mExecCommit是个ruannable线程任务

1
2
3
4
5
6
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};

然后在加入新的退出动画任务

这个任务里执行了execPendingActions()方法,我们继续往下走

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Only call from main thread!
*/
public boolean execPendingActions() {
ensureExecReady(true);

boolean didSomething = false;
while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
mExecutingActions = true;
try {
removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
} finally {
cleanupExec();
}
didSomething = true;
}

doPendingDeferredStart();
burpActive();

return didSomething;
}

我们可以看到这个方法里先执行了ensureExecReady(true)去执行前置的一些操作,然后在while循环里将挂起操作中的所有东西添加到记录, 以及它们是添加还是 pop 操作 isPop,是否需要操作等。然后进入
removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop)方法:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

/**
* Remove redundant BackStackRecord operations and executes them. This method merges operations
* of proximate records that allow reordering. See
* {@link FragmentTransaction#setReorderingAllowed(boolean)}.
* <p>
* For example, a transaction that adds to the back stack and then another that pops that
* back stack record will be optimized to remove the unnecessary operation.
* <p>
* Likewise, two transactions committed that are executed at the same time will be optimized
* to remove the redundant operations as well as two pop operations executed together.
*
* @param records The records pending execution
* @param isRecordPop The direction that these records are being run.
*/
private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records,
ArrayList<Boolean> isRecordPop) {
if (records == null || records.isEmpty()) {
return;
}

if (isRecordPop == null || records.size() != isRecordPop.size()) {
throw new IllegalStateException("Internal error with the back stack records");
}

// Force start of any postponed transactions that interact with scheduled transactions:
executePostponedTransaction(records, isRecordPop);

final int numRecords = records.size();
int startIndex = 0;
for (int recordNum = 0; recordNum < numRecords; recordNum++) {
final boolean canReorder = records.get(recordNum).mReorderingAllowed;
if (!canReorder) {
// execute all previous transactions
if (startIndex != recordNum) {
executeOpsTogether(records, isRecordPop, startIndex, recordNum);
}
// execute all pop operations that don't allow reordering together or
// one add operation
int reorderingEnd = recordNum + 1;
if (isRecordPop.get(recordNum)) {
while (reorderingEnd < numRecords
&& isRecordPop.get(reorderingEnd)
&& !records.get(reorderingEnd).mReorderingAllowed) {
reorderingEnd++;
}
}
executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
startIndex = reorderingEnd;
recordNum = reorderingEnd - 1;
}
}
if (startIndex != numRecords) {
executeOpsTogether(records, isRecordPop, startIndex, numRecords);
}
}

在这方法里,先执行executePostponedTransaction强制的将保存的Transactions除了当前的,其他的清除掉,然后循环取得BackStackRecord的list里的每一个对象来判断是否要执行executeOpsTogether操作

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
* Executes a subset of a list of BackStackRecords, all of which either allow reordering or
* do not allow ordering.
* @param records A list of BackStackRecords that are to be executed
* @param isRecordPop The direction that these records are being run.
* @param startIndex The index of the first record in <code>records</code> to be executed
* @param endIndex One more than the final record index in <code>records</code> to executed.
*/
private void executeOpsTogether(ArrayList<BackStackRecord> records,
ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
final boolean allowReordering = records.get(startIndex).mReorderingAllowed;
boolean addToBackStack = false;
if (mTmpAddedFragments == null) {
mTmpAddedFragments = new ArrayList<>();
} else {
mTmpAddedFragments.clear();
}
mTmpAddedFragments.addAll(mAdded);
Fragment oldPrimaryNav = getPrimaryNavigationFragment();
for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
final BackStackRecord record = records.get(recordNum);
final boolean isPop = isRecordPop.get(recordNum);
if (!isPop) {
oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
} else {
oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav);
}
addToBackStack = addToBackStack || record.mAddToBackStack;
}
mTmpAddedFragments.clear();

if (!allowReordering) {
FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex,
false);
}
executeOps(records, isRecordPop, startIndex, endIndex);

int postponeIndex = endIndex;
if (allowReordering) {
ArraySet<Fragment> addedFragments = new ArraySet<>();
addAddedFragments(addedFragments);
postponeIndex = postponePostponableTransactions(records, isRecordPop,
startIndex, endIndex, addedFragments);
makeRemovedFragmentsInvisible(addedFragments);
}

if (postponeIndex != startIndex && allowReordering) {
// need to run something now
FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
postponeIndex, true);
moveToState(mCurState, true);
}

for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
final BackStackRecord record = records.get(recordNum);
final boolean isPop = isRecordPop.get(recordNum);
if (isPop && record.mIndex >= 0) {
freeBackStackIndex(record.mIndex);
record.mIndex = -1;
}
record.runOnCommitRunnables();
}
if (addToBackStack) {
reportBackStackChanged();
}
}

然后在这个方法里也循环的去取得每一个FragmentTransaction对应操作集的是否是执行出栈分别跳转到对应的方法中执行

(!isPop) {
1
2
3
4
    oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
} else {
oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav);
}

在这些方法中都有大致相同的逻辑,判断Op对象里的对应的状态值进行add,hide,remove等操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
switch (op.cmd) {
case OP_ADD:
case OP_ATTACH:
added.add(op.fragment);
break;
case OP_REMOVE:
case OP_DETACH: {
added.remove(op.fragment);
if (op.fragment == oldPrimaryNav) {
mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, op.fragment));
opNum++;
oldPrimaryNav = null;
}
}
break;

。。。

将各种操作封装成Op并添加,最后在executePopOps方法中通过循环给每个fragment调用setNextAnim设置动画属性。如下:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
void executePopOps(boolean moveToState) {
for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) {
final Op op = mOps.get(opNum);
Fragment f = op.fragment;
if (f != null) {
f.setNextTransition(FragmentManagerImpl.reverseTransit(mTransition),
mTransitionStyle);
}
switch (op.cmd) {
case OP_ADD:
f.setNextAnim(op.popExitAnim);
mManager.removeFragment(f);
break;
case OP_REMOVE:
f.setNextAnim(op.popEnterAnim);
mManager.addFragment(f, false);
break;
case OP_HIDE:
f.setNextAnim(op.popEnterAnim);
mManager.showFragment(f);
break;
case OP_SHOW:
f.setNextAnim(op.popExitAnim);
mManager.hideFragment(f);
break;
case OP_DETACH:
f.setNextAnim(op.popEnterAnim);
mManager.attachFragment(f);
break;
case OP_ATTACH:
f.setNextAnim(op.popExitAnim);
mManager.detachFragment(f);
break;
case OP_SET_PRIMARY_NAV:
mManager.setPrimaryNavigationFragment(null);
break;
case OP_UNSET_PRIMARY_NAV:
mManager.setPrimaryNavigationFragment(f);
break;
default:
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
if (!mReorderingAllowed && op.cmd != OP_REMOVE && f != null) {
mManager.moveFragmentToExpectedState(f);
}
}
if (!mReorderingAllowed && moveToState) {
mManager.moveToState(mManager.mCurState, true);
}
}

当fragmentmanager在moveToState或者completeShowHideFragment等操作时,都会去调用getNextAnim方法获得动画对象,或者通过loadAnimation方法去间接调用getNextAnim方法获得动画对象,最终在后续操作中完成动画的执行。

Unbenannt

Veröffentlicht am 2017-04-06

##hook Instrumentation 实现对Activity生命周期的监听

###如何hook主线程
首先要先找对hook的点,在Application中有一个隐藏的mLoadedApk,它在Application之初便保存了程序的所有信息mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;我们可以看到这个LoadedApk里包含了ActivityThread,这个ActivityThread里又有一个sCurrentActivityThread,sCurrentActivityThread指的是他自己本身,所以这个sCurrentActivityThread就是我们想要hook住的主线程对象,在这个主线程对象里我们又可以hook他的Instrumentation对象以实现对activity对象的各个生命周期的回调的监听。

如代码所示,我们需要在Application启动时去替换主线程的Instrumentation:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.example.zhanqian.myapplication;
import android.app.Activity;
import android.app.Application;
import android.app.Instrumentation;
import android.util.Log;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by zhangqian on 2016/5/27.
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
try {
hookActivity(this);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public void hookActivity( Application application) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class<?> clz = application.getClass();
Field field = clz.getField("mLoadedApk");
field.setAccessible(true);
Class <?> clzz = field.getType();
Field threadF = clzz.getDeclaredField("mActivityThread");
threadF.setAccessible(true);
Class<?> thread = threadF.getType();
Method currentActivityThread= thread.getDeclaredMethod("currentActivityThread");
currentActivityThread.setAccessible(true);
/**获取主线程对象**/
Object activityThreadObject=currentActivityThread.invoke(null);
Field mInstrumentationField = thread.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
Instrumentation mInsertInstrumentation = new InsertInstrumentation();
mInstrumentationField.set(activityThreadObject,mInsertInstrumentation);
}
private static class InsertInstrumentation extends android.app.Instrumentation{
@Override
public void callActivityOnResume(Activity activity) {
super.callActivityOnResume(activity);
Log.i("qinlingrong","---------------hook Activity OnResume -----------------");
}
@Override
public void callActivityOnPause(Activity activity) {
super.callActivityOnPause(activity);
Log.i("qinlingrong","---------------hook Activity OnPause -----------------");
}
}
}

Unbenannt

Veröffentlicht am 2017-04-04

##android屏幕解锁新解

最近因为一些事接触到android屏幕解锁这块,刚开始查询网上资料,绝大部分以keyguardLock 来进行获取屏幕锁和接触屏幕锁,其思路如下:

1
2
3
4
5
6
7
 KeyguardManager keyguardManager = (KeyguardManager)getSystemService(KEYGUARD_SERVICE); 
KeyguardLock keyguardLock = keyguardManager.newKeyguardLock("glapp");
keyguardLock.disableKeyguard();
获取当前屏幕锁并解锁;

KeyguardManager keyguardManager = (KeyguardManager)getSystemService(KEYGUARD_SERVICE);
KeyguardLock keyguardLock = keyguardManager.newKeyguardLock("glapp"); keyguardLock.reenableKeyguard();

然后在相应位置 释放持有的屏幕锁

但是这个思路有一个问题,在最后一次退出应用的时候由于释放的屏幕锁被系统回收,系统会再次锁屏,造成不好的用户体验,

经过翻阅api,发现有一个更好的解决方式:

在window对象中,存在这样一个flags :WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD

位于 package android.view.WindowManager中。

其注释如下:

/** Window flag: when set the window will cause the keyguard to
 * be dismissed, only if it is not a secure lock keyguard.  Because such
 * a keyguard is not needed for security, it will never re-appear if
 * the user navigates to another window (in contrast to
 * {@link #FLAG_SHOW_WHEN_LOCKED}, which will only temporarily
 * hide both secure and non-secure keyguards but ensure they reappear
 * when the user moves to another UI that doesn't hide them).
 * If the keyguard is currently active and is secure (requires an
 * unlock pattern) than the user will still need to confirm it before
 * seeing this window, unless {@link #FLAG_SHOW_WHEN_LOCKED} has
 * also been set.
 */
public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;

其大意为:

窗口的标志: 当键盘锁不是安全的的时候(即基本的屏幕锁)设置该flags后该窗口显示会导致键盘锁被解锁。因为这种键盘锁不需要用于安全,如果用户导航到另一个窗口,它(键盘锁)将永远不会重新出现(相对于{@link #FLAG_SHOW_WHEN_LOCKED},这只是暂时隐藏安全和非安全的 keyguards,但r如果系统确保当用户移到另一个用户界面(即不在这个应用中),并不设置他们显示方式的时候,它们(键盘锁)又会出现(即键盘锁又恢复到了原来的正常状态而不是被获取释放等等))。如果键盘锁当前处于活动状态,并且是安全的 (比如手势屏幕解锁图案) 用户在看到自己的界面前仍然将需要进行解锁,除非用户还设置了 {@link #FLAG_SHOW_WHEN_LOCKED}。

So 用法如下 在你的oncreate()方法中,在你setContentView之前
你只需要为 window 添加相应flags:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON|WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
setContentView(R.layout.lockscreen);
即可大功告成!

Unbenannt

Veröffentlicht am 2017-03-17

初探Fragment

Fragment是什么

  • Fragmeng简介:

自从Android 3.0中引入fragments 的概念,根据词海的翻译可以译为:碎片、片段。其上的是为了解决不同屏幕分辩率的动态和灵活UI设计。大屏幕如平板小屏幕如手机,平板电脑的设计使得其有更多的空间来放更多的UI组件,而多出来的空间存放UI使其会产生更多的交互,从而诞生了fragments 。fragments 的设计不需要你来亲自管理view hierarchy 的复杂变化,通过将Activity 的布局分散到frament 中,可以在运行时修改activity 的外观,并且由activity 管理的back stack 中保存些变化。

  • Fragmeng优点:

Fragment可以使你能够将activity分离成多个可重用的组件,每个都有它自己的生命周期和UI。

Fragment可以轻松得创建动态灵活的UI设计,可以适应于不同的屏幕尺寸。从手机到平板电脑。

Fragment是一个独立的模块,紧紧地与activity绑定在一起。可以运行中动态地移除、加入、交换等。

Fragment提供一个新的方式让你在不同的安卓设备上统一你的UI。

Fragment 解决Activity间的切换不流畅,轻量切换。

Fragment 替代TabActivity做导航,性能更好。

Fragment 在4.2.版本中新增嵌套fragmeng使用方法,能够生成更好的界面效果。

Fragment做局部内容更新更方便,原来为了到达这一点要把多个布局放到一个activity里面,现在可以用多Fragment来代替,只有在需要的时候才加载Fragment,提高性能

  • Fragmeng使用
  1. 两种添加方法

在activity的layout文件中显式的添加一个fragment

通过代码将fragment添加到一个已存在的ViewGroup,例如viewpager或者一个FrameLayout

  1. Fragmeng与Activity交互

Fragmeng通过getActivity()获得所在Activity上下文,需要注意的是getAcitivy()有可能为空

解决办法:

更”安全”的方法:(对于Fragment已经onDetach这种情况,我们应该避免在这之后再去调用宿主Activity对象,比如取消这些异步任务,但我们的团队可能会有粗心大意的情况,所以下面给出的这个方案会保证安全)
在Fragment基类里设置一个Activity mActivity的全局变量,在onAttach(Activity activity)里赋值,使用mActivity代替getActivity(),保证Fragment即使在onDetach后,仍持有Activity的引用(有引起内存泄露的风险,但是异步任务没停止的情况下,本身就可能已内存泄漏,相比Crash,这种做法“安全”些),即:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected Activity mActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.mActivity = activity;
}

/**
* 如果你用了support 23的库,上面的方法会提示过时,有强迫症的小伙伴,可以用下面的方法代替
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.mActivity = (Activity)context;
}

Activity通过getFragmentManager()获得Fragmeng实例(findFragmentById,findFragmentByTag);

通过FragmentTransaction可以增加、移除或者代替Fragments;

通过fragmentTransaction.addToBackStack()可以把fragmeng保存到栈,响应后退按钮;

Activity与fragment相互调用方法接口还可以通过Activity的onAttachFragment方法获得fragment的实例从而调用fragment的接口方法,而fragment可以通过onAttach获得父Acitivy的实例来调用相应的方法接口。如下:

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
import android.app.Fragment;
/**
* @author:zhangqian
* @time:2017/3/17
*/

public class TestActivity extends Activity {



private FragmentListener listener;

@Override
public void onAttachFragment(Fragment fragment) {

try {
if(fragment instanceof TestFragment){
listener = (TestFragment)fragment;
listener.todo();
}
}catch (Exception e){
e.printStackTrace();
}
super.onAttachFragment(fragment);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @author:zhangqian
* @time:2017/3/17
*/

public class TestFragment extends Fragment implements FragmentListener {

private MainActivity mActivity;

@Override
public void onAttach(Context context) {
super.onAttach(context);
mActivity = (MainActivity) context;
mActivity.getData();
}

@Override
public void todo() {

}
}

Qinnlingrong

4 Artikel
© 2018 Qinnlingrong
Erstellt mit Hexo
|
Theme — NexT.Muse v5.1.4