国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Lottie支持復雜動畫(json)使用筆記

2019-11-09 16:42:07
字體:
來源:轉載
供稿:網友

Lottie支持復雜動畫(json)使用筆記

基礎信息

Lottie Git開源地址(都給出只是方便大家找,其實我本人只用到Android) Android:地址iOS:地址React Native:地址JSON文件需要Bodymovin導出的json文件 Bodymovin:地址官方說明:地址官方Demo(Google市場):地址

Lottie相關信息

官方案例丟在Google應用市場了,國內不好下載。所以干脆自己打包Demo 百度云下載 如果是要研究代碼,又不想用Git,可以自己反編譯看看我下載它是因為json動畫文件不好弄:說說我怎么取的資源吧,盡管大多數都知道。 直接apk擴展名改成zip使用壓縮文件打開assets目錄里面全部是需要的文件我也準備了百度云鏈接

Lottie使用筆記

設置動畫文件,優先匹配代碼,代碼沒設置,顯示的才會是布局文件的配置。

初始化配置

Lottie要求最低編輯版本是16(Android4.1)

minSdkVersion 16

Gradle注冊添加支持

dependencies { compile 'com.airbnb.android:lottie:1.0.1'}添加json動畫文件到資產目錄(app/src/main/assets)

給使用到該控件的布局文件根標簽添加(如果你在布局文件設置的話,如果沒有,請忽略)

xmlns:app="http://schemas.android.com/apk/res-auto"

展示動畫

布局文件

/** * lottie_fileName json文件名 * lottie_loop 是否循環播放 * lottie_autoPlay 是否自動播放<com.airbnb.lottie.LottieAnimationView android:id="@+id/animation_view" android:layout_width="wrap_content" android:layout_height="wrap_content" app:lottie_fileName="hello-world.json" app:lottie_loop="true" app:lottie_autoPlay="true" />

代碼實現

LottieAnimationView animationView = (LottieAnimationView) findViewById(R.id.animation_view);// 設置json文件animationView.setAnimation("helloworld.json");// 設置是否循環播放animationView.loop(true);// 播放動畫animationView.playAnimation();// 暫停動畫:貌似有點不同animationView.cancelAnimation();// 停止動畫:我感覺兩個效果順序是顛倒的,使用到時候請測試看看吧animationView.pauseAnimation();// 跳轉進度(0.0-1.1)animationView.setPRogress(float f);// 在監聽中可以添加代碼設置動畫時長animator.setDuration(1000L);

切換動畫

// 最簡單的,但是需要注意,只適用于小Json文件,大的Json加載時間過長,中間可能空出來。// animationView.setAnimation("LottieLogo2.json");// animationView.playAnimation();// 官方還給出另外一種標準的切換方式LottieComposition.fromAssetFileName(act, "LottieLogo2.json", new LottieComposition.OnCompositionLoadedListener() { @Override public void onCompositionLoaded(LottieComposition composition) { animationView.setComposition(composition); animationView.playAnimation(); } });

設置監聽

// 播放的文件更新的時候,也可以理解每一幀都調用,沒想到應用場景,反正更一個動畫就不停的調用。animationView.addAnimatorUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { }});// 常用的監聽,很多都很有用處。animationView.addAnimatorListener(new Animator.AnimatorListener() { // 動畫開始調用 @Override public void onAnimationStart(Animator animator) { } // 如果設置loop為true,永遠不會調用 @Override public void onAnimationEnd(Animator animator) { } // 動畫取消監聽,監聽的是Cancel方法,可是還是進度條暫停的狀態。 @Override public void onAnimationCancel(Animator animator) { } // 動畫重復,第一次播放不是重復,不包含在內,切換動畫也一樣。 @Override public void onAnimationRepeat(Animator animator) { }});

本地文件展示

這個可以直接打開系統的文件管理器

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType("*/*");intent.addCategory(Intent.CATEGORY_OPENABLE);try { startActivityForResult(Intent.createChooser(intent, "請選擇一個JSON文件"), PLAYER_BY_FILE);} catch (android.content.ActivityNotFoundException ex) { Toast.makeText(act, "請安裝一個文件管理器。", Toast.LENGTH_SHORT).show();}

在這里接收選擇的文件

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) { if(requestCode == PLAYER_BY_FILE){ Uri uri = data.getData(); InputStream fis; try { switch (uri.getScheme()) { case "file": fis = new FileInputStream(uri.getPath()); break; case "content": fis = act.getContentResolver().openInputStream(uri); break; default: Toast.makeText(act, "加載失敗!", Toast.LENGTH_SHORT).show(); return; } } catch (FileNotFoundException e) { Toast.makeText(act, "請安裝一個文件管理器。", Toast.LENGTH_SHORT).show(); return; } }}

根據返回的 輸入流 InputStream 來展示Json動畫

LottieComposition .fromInputStream(act, fis, new LottieComposition.OnCompositionLoadedListener() { @Override public void onCompositionLoaded(LottieComposition composition) { animationView.setComposition(composition); animationView.playAnimation(); } });

根據網絡展示

// str 就是聯網請求到的json字符串JSONObject jsonObject = null;try { jsonObject = new JSONObject(str);} catch (JSONException e) { e.printStackTrace();}LottieComposition .fromJson(getResources(), jsonObject, new LottieComposition.OnCompositionLoadedListener() { @Override public void onCompositionLoaded(LottieComposition composition) { animationView.setComposition(composition); animationView.playAnimation(); } });

引導界面動畫

這個,建議別看官方的Demo,引用第三方的工具類,反正我沒用過那個類,只能一行一行的分析。最后,我發現實際上只是做了一個ViewPager的滑動監聽,他之所以用那個是為了美觀。如果誰有興趣,可以使用一下試試看。

布局文件使用RelativeLayout,在 LottieAnimationView 上面添加一個ViewPager

<?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_change_pager" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="ll.withwings.testlottieanimation.lottie.ChangePagerActivity"> <com.airbnb.lottie.LottieAnimationView android:id="@+id/animation_view" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <android.support.v4.view.ViewPager android:id="@+id/vp_show_animation" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v4.view.ViewPager></RelativeLayout>

代碼方面設置監聽ViewPager滑動。lerp方法可以根據自己喜歡修改速度。

// ViewPager 使用透明的Fragment填充// 設置 LottieAnimationView 動畫的進度與ViewPager聯動/** * 這里之所以多一個1f,是為了ViewPager最后一個item不能滑動準備的(值是根據EmptyFragment數量計算的) */private static final float[] ANIMATION_TIMES = new float[]{ 0f, 0.3333f, 0.6666f, 1f, 1f};/** * 為了ViewPager聯動效果準備的空Fragment。 */private List<EmptyFragment> emptyFragments;……mVpShowAnimation.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { setAnimationProgress(position, positionOffset); } @Override public void onPageSelected(int position) { } @Override public void onPageScrollStateChanged(int state) { }});private void setAnimationProgress(int position, float positionOffset) { float startProgress = ANIMATION_TIMES[position]; float endProgress = ANIMATION_TIMES[position + 1]; // 更新動畫進度 animationView.setProgress(lerp(startProgress, endProgress, positionOffset));}// 根據ViewPager拖動偏移比例來計算位置private float lerp(float startValue, float endValue, float f) { return startValue + f * (endValue - startValue);}

字母特效動畫

如果誰用到的話,我建議是用我這個代碼,官方代碼為了兼容性刪減了很多功能。當然,如果用官方的,只需要復制官方Git里面 LottieFontViewGroup 這個文件即可

* 首先復制文件 LottieFontViewGroup.java 到自己工程* 需要記得加上監聽,onDestroy 時移除監聽。可以讓控件根據輸入內容自動滾動。 @Override protected void initListener() { // 這個監聽可以根據換行自動滑動 fontView.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener); } @Override protected void onDestroy() { // add的監聽還要刪除 fontView.getViewTreeObserver().removeOnGlobalLayoutListener(layoutListener); super.onDestroy(); } // 監聽操作,建議直接復制走。 private final ViewTreeObserver.OnGlobalLayoutListener layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { scrollView.fullScroll(View.FOCUS_DOWN); } };

附上我改的:

public class LottieFontViewGroup extends FrameLayout { private final Map<String, LottieComposition> compositionMap = new HashMap<>(); private final List<View> views = new ArrayList<>(); @Nullable private LottieAnimationView cursorView; public LottieFontViewGroup(Context context) { super(context); init(); } public LottieFontViewGroup(Context context, AttributeSet attrs) { super(context, attrs); init(); } public LottieFontViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { setFocusableInTouchMode(true); LottieComposition.fromAssetFileName(getContext(), "Mobilo/BlinkingCursor.json", new LottieComposition.OnCompositionLoadedListener() { @Override public void onCompositionLoaded(LottieComposition composition) { cursorView = new LottieAnimationView(getContext()); cursorView.setLayoutParams(new LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT )); cursorView.setComposition(composition); cursorView.loop(true); cursorView.playAnimation(); addView(cursorView); } }); } /** * 根據當前狀態更新軟鍵盤狀態 */ public void changeInputType() { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); } /** * 判斷軟鍵盤狀態 * * @return true代表打開,false代表隱藏 */ public boolean getInputType() { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); boolean isOpen = imm.isActive();//isOpen若返回true,則表示輸入法打開 return isOpen; } /** * 更改軟鍵盤顯示 * * @param isOpen */ public void setInputType(boolean isOpen) { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); if (isOpen) { imm.showSoftInput(this, InputMethodManager.SHOW_FORCED); } else { // 強制隱藏鍵盤 imm.hideSoftInputFromWindow(this.getWindowToken(), 0); } } private String string = ""; /** * 獲取當前字符串。 */ public String getString() { return string; } /** * ASCII 碼轉字符串 * * @param ascii * @return 文件字符串 */ public String asciiToString(int ascii) { StringBuffer sbu = new StringBuffer(string); sbu.append((char) ascii); return new String(sbu); } private float downX; private float downY; /** * 點擊控件,切換軟鍵盤顯示 * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { if(event.getAction()==MotionEvent.ACTION_DOWN){ downX = event.getX(); downY = event.getY(); }else if(event.getAction() == MotionEvent.ACTION_UP && event.getX()== downX && event.getY() == downY){ changeInputType(); } return true; } private void addSpace() { int index = indexOfChild(cursorView); addView(createSpaceView(), index); } @Override public void addView(View child, int index) { super.addView(child, index); if (index == -1) { views.add(child); } else { views.add(index, child); } } private void removeLastView() { if (views.size() > 1) { int position = views.size() - 2; removeView(views.get(position)); views.remove(position); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (views.isEmpty()) { return; } int currentX = getPaddingTop(); int currentY = getPaddingLeft(); for (int i = 0; i < views.size(); i++) { View view = views.get(i); if (!fitsOnCurrentLine(currentX, view)) { if (view.getTag() != null && view.getTag().equals("Space")) { continue; } currentX = getPaddingLeft(); currentY += view.getMeasuredHeight(); } currentX += view.getWidth(); } setMeasuredDimension(getMeasuredWidth(), currentY + views.get(views.size() - 1).getMeasuredHeight() * 2); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (views.isEmpty()) { return; } int currentX = getPaddingTop(); int currentY = getPaddingLeft(); for (int i = 0; i < views.size(); i++) { View view = views.get(i); if (!fitsOnCurrentLine(currentX, view)) { if (view.getTag() != null && view.getTag().equals("Space")) { continue; } currentX = getPaddingLeft(); currentY += view.getMeasuredHeight(); } view.layout(currentX, currentY, currentX + view.getMeasuredWidth(), currentY + view.getMeasuredHeight()); currentX += view.getWidth(); } } @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { BaseInputConnection fic = new BaseInputConnection(this, false); outAttrs.actionLabel = null; outAttrs.inputType = InputType.TYPE_NULL; outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT; return fic; } @Override public boolean onCheckIsTextEditor() { return true; } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_SPACE) { string += " "; addSpace(); return true; } if (keyCode == KeyEvent.KEYCODE_DEL) { if(string.length()>0){ string = string.substring(0, string.length() - 1); }else{ string = ""; } removeLastView(); return true; } if (!isValidKey(event)) { return super.onKeyUp(keyCode, event); } String letter = "" + Character.toUpperCase((char) event.getUnicodeChar()); // switch (letter) { // case ",": // letter = "Comma"; // break; // case "'": // letter = "Apostrophe"; // break; // case ";": // case ":": // letter = "Colon"; // break; // } final String fileName = "Mobilo/" + letter + ".json"; if (compositionMap.containsKey(fileName)) { addComposition(compositionMap.get(fileName)); } else { LottieComposition.fromAssetFileName(getContext(), fileName, new LottieComposition.OnCompositionLoadedListener() { @Override public void onCompositionLoaded(LottieComposition composition) { compositionMap.put(fileName, composition); addComposition(composition); } }); } return true; } private boolean isValidKey(KeyEvent event) { if (!event.hasNoModifiers()) { return false; } if (event.getKeyCode() >= KeyEvent.KEYCODE_A && event.getKeyCode() <= KeyEvent.KEYCODE_Z) { string = asciiToString(event.getKeyCode() + 36); return true; } // switch (keyCode) { // case KeyEvent.KEYCODE_COMMA: // case KeyEvent.KEYCODE_APOSTROPHE: // case KeyEvent.KEYCODE_SEMICOLON: // return true; // } return false; } private void addComposition(LottieComposition composition) { LottieAnimationView lottieAnimationView = new LottieAnimationView(getContext()); lottieAnimationView.setLayoutParams(new LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT )); lottieAnimationView.setComposition(composition); lottieAnimationView.playAnimation(); if (cursorView == null) { addView(lottieAnimationView); } else { int index = indexOfChild(cursorView); addView(lottieAnimationView, index); } } private boolean fitsOnCurrentLine(int currentX, View view) { return currentX + view.getMeasuredWidth() < getWidth() - getPaddingRight(); } private View createSpaceView() { View spaceView = new View(getContext()); spaceView.setLayoutParams(new LayoutParams( getResources().getDimensionPixelSize(R.dimen.font_space_width), ViewGroup.LayoutParams.WRAP_CONTENT )); spaceView.setTag("Space"); return spaceView; }}

Lottie使用出現問題

JSON文件不播放,比如:代碼設置文件應用崩潰,布局文件設置了無效。

JSON文件有有格式要求的,這點我重復一下,Lottie支持的Json文件來自于Bodymovin,該項目是Adobe公司的動畫制作軟件After Effects的插件,用來將動畫導出成 svg/canvas/html + js ,方便在瀏覽器上展示。

打開界面就崩:json文件錯誤。路徑是直接跟目錄下,直接文件名;根目錄的文件夾,是文件夾名/文件名,如:

Logo/LogoSmall.jsonLogoSmall.json

loop設置為false 之后,播放結束無法再次開始,請在監聽的結束監聽中添加:

animationView.pauseAnimation();
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 新野县| 阿坝县| 松原市| 芷江| 荔浦县| 安国市| 阿瓦提县| 南木林县| 祁东县| 土默特右旗| 商南县| 岱山县| 格尔木市| 罗田县| 哈尔滨市| 祁阳县| 永善县| 玛纳斯县| 宣恩县| 开平市| 象山县| 策勒县| 梓潼县| 万载县| 桂阳县| 河西区| 高陵县| 疏附县| 西峡县| 平原县| 泽州县| 聂拉木县| 秦安县| 南宫市| 松滋市| 慈溪市| 三亚市| 房山区| 西城区| 响水县| 望城县|