編輯:關於Android編程
曾經(或者現在)很多人說起Android和iOS都會拿Android的UI設計來開黑, “你看看人家iOS的設計, 再來看看Android的, 差距怎麼就這麼大呢?”, 對於這種說辭, 可以一句話來總結一下”他們還停留在4.X之前的時代”. 自從Android5.0推出Material Design設計規范後, Android在設計上早已甩那個萬年不變的iOS好幾十條街!
以上純屬個人看法, 請勿開黑~~, 下面進入今天的主題.
還記得我曾經有篇文章(你所不知道的Activity轉場動畫——ActivityOptions)是來介紹Android新的轉場動畫的(對於
ActivityOptions還不太熟悉的朋友現在可以打開上面的文章先來了解下), 那篇文章中介紹的Android預定義的幾個轉場雖然在效果上已經很贊了, 但是還是很難滿足我們在開發中遇到的各種需求, 那怎麼辦?
View不能滿足需求, 我們可以自定義,
Transition也是一樣~~, 所以這篇文章我們就來介紹一下如何自定義
Transition動畫.
熟悉原理
在開始自定義之前, 我們首先來簡單的了解一下
Transition轉場動畫的原理, 大家在看到你所不知道的Activity轉場動畫——ActivityOptions這篇文章時, 對Android提供的這種新的轉場動畫都震撼到了, 但是肯定有很多人對它的原理不是很請求, 尤其是
Scene場景動畫, 一個ImageView怎麼就變著變著跳轉到其他的Activity了呢? 其實它的原理很簡單,Transition動畫其實就是拿著第一頁某個view的信息去第二頁的某個view上做的動畫, 這樣我們在視覺上就會產生一個漸變的錯覺~
玩玩Transition
在稍微了解了一下原理之後, 我們就來玩玩
Transition了, 如何自定義一個
Transition呢? 跟自定義view我們需要繼承View或者ViewGroup一樣, 這裡我們需要繼承
Transition類.
public class MyTransition extends Transition {}
有兩個抽象方法必須要要重寫,
public class MyTransition extends Transition {
@Override
public void captureStartValues(TransitionValues transitionValues) {
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
}
}
除了這兩個必須要重寫的方法, 我們還要重寫一個
createAnimator方法來自定義動畫, 於是, 我們要自定義一個
Transition, 一個類的結構肯定是肯定是這樣的.
public class MyTransition extends Transition {
@Override
public void captureStartValues(TransitionValues transitionValues) {
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, final TransitionValues endValues) {
}
}
ok, 下面我們來詳細說一下這三個方法都是用來干嘛的.
首先captureStartValues, 從字面上來看是用來收集開始信息的, 什麼開始信息? 當然是動畫的開始信息了. 那同樣的
captureEndValues是用來收集動畫結束的信息的. 收集完了信息,就要通過
createAnimator來創建個
Animator供系統調用了.
再來看看
TransitionValues這個陌生的類, 這個類其實很簡單, 只有兩個成員變量
view和
values,
view指的是我們要從哪個view上收集信息,
values是用來存放我們收集到的信息的. 比如: 在
captureStartValues裡,
transitionValues.view指的就是我們在開始動畫的界面上的那個view, 在
captureEndValues指的就是在目標界面上的那個view.
好了, 上面幾個方法的作用介紹完畢後, 我們馬上就來完成一個進入消息內容的動畫效果, 還是老規矩, 在開始代碼之前, 我們先來看看效果.
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPrCmLCDU2nVidW50dcnPwrzGwdPQteO30b6iLCDQp7n7srvVprXYLCC01bvu18W/tCwgu/LV37/J0tTU2s7E1cLX7rrztcTBtL3T19S8us/C1Nixvs7EtcRkZW1v1LTC69fUvLrUy9DQv7R+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L3A+DQo8cD48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT48Y29kZT7X0M+4uduy7NCnufssIM7Sw8e/ydLU1dK1vcG9tKa2r7utLjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9wPg0KPGJsb2NrcXVvdGU+DQoJPGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+PGNvZGU+taXQ0MTayN2008v81NrB0LHt1tC1xM671sPSxravtb2958PmtcTX7snPw+YuIM/7z6K1xMTayN3TybWl0NDW8L2l1bm/qi4g1eLBvbj2tq+7rcrHy7PQ8ta00NC1xCA8L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvYmxvY2txdW90ZT4NCjxwPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPs2ouf3Jz8PmtcS31s72LCDO0sPHtPPWwr/J0tS1w7P2LCDPwsPmLCDO0sPH0OjSqsrVvK+1xNDFz6LT0DxzdHJvbmc+dmlld9TavefD5rXEzrvWwzwvc3Ryb25nPrrNPHN0cm9uZz52aWV3tcS437bI0MXPojwvc3Ryb25nPiwgy/nS1M7Sw8fPyMC0tqjS5dK7z8LQ6NKqytW8r7XE0MXPojwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9wPg0KPHByZSBjbGFzcz0="brush:java;">
public class MyTransition extends Transition {
private static final String TOP = "top";
private static final String HEIGHT = "height";
// ...
}
然後我們開始收集動畫開始需要的信息
public class MyTransition extends Transition {
private static final String TOP = "top";
private static final String HEIGHT = "height";
@Override
public void captureStartValues(TransitionValues transitionValues) {
View view = transitionValues.view;
Rect rect = new Rect();
view.getHitRect(rect);
transitionValues.values.put(TOP, rect.top);
transitionValues.values.put(HEIGHT, view.getHeight());
Log.d("qibin", "start:" + rect.top + ";" + view.getHeight());
}
}
首先, 我們通過
transitionValues.view拿到我們要收集信息的目標view, 然後我們可以通過
getHitRect可以拿到它在ListView中的上下左右信息, 最後我們通過
transitionValues.values.put(TOP, rect.top)來保存一下他距離父布局上面的距離, 當然我們還需要通過
transitionValues.values.put(HEIGHT, view.getHeight())來保存動畫初始的高度.
收集完動畫開始的信息, 我們再來收集動畫結束的信息, 依葫蘆畫瓢, 很快就能寫出下面的代碼.
public class MyTransition extends Transition {
private static final String TOP = "top";
private static final String HEIGHT = "height";
@Override
public void captureEndValues(TransitionValues transitionValues) {
transitionValues.values.put(TOP, 0);
transitionValues.values.put(HEIGHT, transitionValues.view.getHeight());
Log.d("qibin", "end:" + 0 + ";" + transitionValues.view.getHeight());
}
}
這裡的代碼和上面並無差別, 動畫結束後, view距離上面的距離應該是0, 不過這裡需要注意的是
captureStartValues方法裡的
transitionValues.view是我們頁面跳轉開始那個界面上的view, 而
captureEndValues方法裡的
transitionValues.view是我們跳轉目標上的view, 所以這兩個方法裡獲取到的view的高度肯定是不一樣的.
好了, 在完成信息收集之後, 我們就來寫動畫效果了,
public class MyTransition extends Transition {
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, final TransitionValues endValues) {
if (startValues == null || endValues == null) { return null;}
final View endView = endValues.view;
final int startTop = (int) startValues.values.get(TOP);
final int startHeight = (int) startValues.values.get(HEIGHT);
final int endTop = (int) endValues.values.get(TOP);
final int endHeight = (int) endValues.values.get(HEIGHT);
ViewCompat.setTranslationY(endView, startTop);
endView.getLayoutParams().height = startHeight;
endView.requestLayout();
ValueAnimator positionAnimator = ValueAnimator.ofInt(startTop, endTop);
if (mPositionDuration > 0) { positionAnimator.setDuration(mPositionDuration);}
positionAnimator.setInterpolator(mPositionInterpolator);
positionAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int current = (int) valueAnimator.getAnimatedValue();
ViewCompat.setTranslationY(endView, current);
}
});
ValueAnimator sizeAnimator = ValueAnimator.ofInt(startHeight, endHeight);
if (mSizeDuration > 0) { sizeAnimator.setDuration(mSizeDuration);}
sizeAnimator.setInterpolator(mSizeInterpolator);
sizeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int current = (int) valueAnimator.getAnimatedValue();
endView.getLayoutParams().height = current;
endView.requestLayout();
}
});
AnimatorSet set = new AnimatorSet();
set.play(sizeAnimator).after(positionAnimator);
return set;
}
}
在說原理的時候, 我們提到過, 這一系列的動畫其實在我們跳轉後的界面上完成的, 所以這裡的動畫我們也是在目標view上完成. 上面兩個方法中收集到的信息, 我們需要在這裡用到, 所以我們通過以下代碼來獲取收集到的信息.
final int startTop = (int) startValues.values.get(TOP);
final int startHeight = (int) startValues.values.get(HEIGHT);
final int endTop = (int) endValues.values.get(TOP);
final int endHeight = (int) endValues.values.get(HEIGHT);
startValues和
endValues都是
createAnimator的參數.
接著幾行莫名奇妙的代碼
ViewCompat.setTranslationY(endView, startTop);
endView.getLayoutParams().height = startHeight;
endView.requestLayout();
是因為我們的動畫順序是先移動, 後展開, 首先把view的高度設置為前一個界面上view的高度是為了防止在移動的過程中view的高度是他自身的高度的.
接著我們創建了兩個動畫, 這兩個動畫很好理解, 一個位移的,一個是展開的, 不過這裡我們給了動畫一個時長和插值器, 這兩個信息是公開給調用者去設置的.
最後我們創建一個AnimatorSet, 在這個動畫集合中, 我們先來完成
sizeAnimator然後開始
positionAnimator, 最後返回該動畫集合. 自定義
Transition完畢.
使用自定義Transition
上面我們完成了
Transition的自定義, 這裡我們就來用一下它, 首先我們要在應用的主題中指定可以使用場景過度動畫.
- true
看過你所不知道的Activity轉場動畫——ActivityOptions這篇文章的朋友都應該清楚, 我們還需要給我們兩個activity中的view一個
transitionName, 這裡就不貼代碼了, 然後我們就來看看如何做跳轉.
public class MainActivity extends AppCompatActivity {
private ListView mListView;
private Adapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.list);
mAdapter = new Adapter();
mListView.setAdapter(new Adapter());
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView adapterView, View view, int position, long id) {
startActivity(view, mAdapter.getItem(position));
}
});
}
public void startActivity(View view, String content) {
Intent intent = new Intent(this, MessageActivity.class);
intent.putExtra("msg", content);
ActivityOptionsCompat compat = ActivityOptionsCompat
.makeSceneTransitionAnimation(this, view, view.getTransitionName());
ActivityCompat.startActivity(this, intent, compat.toBundle());
}
}
跳轉的代碼大家都可以在你所不知道的Activity轉場動畫——ActivityOptions這篇文章中找到, 這裡就不解釋了, 我們主要還是來看看在目標activity中怎麼應用動畫.
public class MessageActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.message_layout);
setTitle("Content");
TextView msgTextView = (TextView) findViewById(R.id.msg);
msgTextView.setText(getIntent().getStringExtra("msg"));
executeTransition();
}
public void executeTransition() {
MyTransition transition = new MyTransition();
transition.setPositionDuration(300);
transition.setSizeDuration(300);
transition.setPositionInterpolator(new FastOutLinearInInterpolator());
transition.setSizeInterpolator(new FastOutSlowInInterpolator());
transition.addTarget("message");
getWindow().setSharedElementEnterTransition(transition);
}
@Override
public void onBackPressed() {
finish();
}
}
來看
executeTransition方法, 在這個方法中, 首頁我們構建了一個我們自定義的
transition, 然後各種配置, 解析來的一行代碼,
transition.addTarget("message");
這個
message就是我們前面提到的
transitionName, 最後我們通過
getWindow().setSharedElementEnterTransition(transition);
來設置進入的動畫.
ok, 現在我們來看看效果
閃爍問題
看到效果後, 細心的朋友可能發現, 在動畫執行的過程中我們的
NavigationBar會產生一個閃爍的效果, 這個效果不是我們想要的,出現這個問題的原因是共享元素動畫是在整個窗口的view上執行的, 在這裡找到了解決方案. 他的解決辦法是: 首先將NavigationBar也作為動畫的一部分, 然後在目標activity中延遲動畫的執行. google給我們提供了兩個方法來用,
postponeEnterTransition()和
startPostponedEnterTransition()方法來延遲動畫的執行.
所以, 現在我們的跳轉代碼應該是這樣的.
public void startActivity(View view, String content) {
View statusBar = findViewById(android.R.id.statusBarBackground);
View navigationBar = findViewById(android.R.id.navigationBarBackground);
List> pairs = new ArrayList<>();
pairs.add(Pair.create(statusBar, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME));
pairs.add(Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
pairs.add(Pair.create(view, view.getTransitionName()));
Intent intent = new Intent(this, MessageActivity.class);
intent.putExtra("msg", content);
ActivityOptionsCompat compat = ActivityOptionsCompat
.makeSceneTransitionAnimation(this, pairs.toArray(new Pair[pairs.size()]));
ActivityCompat.startActivity(this, intent, compat.toBundle());
}
在目標activity中執行的動畫的代碼也應該是這樣的.
public void executeTransition() {
postponeEnterTransition();
final View decorView = getWindow().getDecorView();
getWindow().getDecorView().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
decorView.getViewTreeObserver().removeOnPreDrawListener(this);
supportStartPostponedEnterTransition();
return true;
}
});
MyTransition transition = new MyTransition();
transition.setPositionDuration(300);
transition.setSizeDuration(300);
transition.setPositionInterpolator(new FastOutLinearInInterpolator());
transition.setSizeInterpolator(new FastOutSlowInInterpolator());
transition.addTarget("message");
getWindow().setSharedElementEnterTransition(transition);
}
到現在, 我們就完美解決了閃爍的問題~. ok, 到這裡, 大家應該可以隨意的自定義
Transition動畫啦~最後需要demo的朋友可以到https://github.com/qibin0506/TransitionAnimator來下載.
本文實例為大家分享了Android錄制音頻文件的具體代碼,供大家參考,具體內容如下1、這個demo中沒有對多次點擊同一個聲音文件做詳細處理,偶爾會有崩潰,用的時候需要注意
當出現崩潰,軟件不會閃退,會出現彈出一個對話框,異常錯誤信息會自動保存在sd卡crash這個文件夾下。後續需要還可以發送到服務器的。看效果圖。1、實現效果圖2、全局異常捕
寫在前言的話:博主的身份如昵稱所寫(java培訓中的菜雞,對編程有興趣,自學android);此博客所有文章就是博主自己從網上學習,自己實踐後的理解,如有不對的地方歡迎指
在本篇博客中,我們分析一下Android中的APK是如何安裝的,以及PKMS在這個過程中進行了哪些工作。APK的安裝方式有很多,我們先來看看如何用adb命令進行安裝。我們