Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make LottieAnimationView reusable #7

Merged
merged 1 commit into from
Nov 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.airbnb.lottie.samples;

import android.content.Context;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

class AssetUtils {

static List<String> getJsonAssets(Context context, String path) throws IOException {
String[] assetList = context.getAssets().list(path);
List<String> files = new ArrayList<>();
for (String asset : assetList) {
if (asset.toLowerCase().endsWith(".json")) {
files.add(asset);
}
}
return files;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package com.airbnb.lottie.samples;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.util.Pair;
import android.support.v7.widget.AppCompatSeekBar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.ToggleButton;

import com.airbnb.lottie.LottieAnimationView;

import java.io.IOException;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnCheckedChanged;
import butterknife.OnClick;

import static com.airbnb.lottie.samples.R.id.play;

public class CycleFragment extends Fragment {

static CycleFragment newInstance() {
return new CycleFragment();
}

@BindView(R.id.animation_view) LottieAnimationView animationView;
@BindView(R.id.seek_bar) AppCompatSeekBar seekBar;
@BindView(play) Button playButton;
@BindView(R.id.loop_button) ToggleButton loopButton;
@BindView(R.id.frames_per_second) TextView fpsView;
@BindView(R.id.dropped_frames) TextView droppedFramesView;
@BindView(R.id.dropped_frames_per_second) TextView droppedFramesPerSecondView;

private List<String> jsonAssets;
private int nextAssetIndex;

private boolean canceled;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_cycle, container, false);
ButterKnife.bind(this, view);

updatePlayButtonText();
animationView.addAnimatorListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
startRecordingDroppedFrames();
}

@Override
public void onAnimationEnd(Animator animation) {
recordDroppedFrames();
if (!canceled) {
animationView.post(new Runnable() {
@Override
public void run() {
playNext();
}
});
}
canceled = false;
}

@Override
public void onAnimationCancel(Animator animation) {
canceled = true;
}

@Override
public void onAnimationRepeat(Animator animation) {
recordDroppedFrames();
startRecordingDroppedFrames();
}
});
animationView.addAnimatorUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
seekBar.setProgress((int) (animation.getAnimatedFraction() * 100));
}
});

seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
animationView.setProgress(progress / 100f);
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});

try {
jsonAssets = AssetUtils.getJsonAssets(getContext(), "");
} catch (IOException e) {
//noinspection ConstantConditions
Snackbar.make(container, R.string.invalid_assets, Snackbar.LENGTH_LONG).show();
}

setNextAnimation();

return view;
}

private void setNextAnimation() {
if (jsonAssets.isEmpty()) {
return;
}
animationView.setAnimation(jsonAssets.get(nextAssetIndex));
nextAssetIndex = (nextAssetIndex + 1) % jsonAssets.size();
}

private void playNext() {
setNextAnimation();
animationView.playAnimation();
}

@Override
public void onStop() {
animationView.cancelAnimation();
super.onStop();
}

@OnClick(play)
public void onPlayClicked() {
if (animationView.isAnimating()) {
animationView.cancelAnimation();
updatePlayButtonText();
} else {
animationView.playAnimation();
updatePlayButtonText();
}
}

private void updatePlayButtonText() {
playButton.setText(animationView.isAnimating() ? "Cancel" : "Play");
}

@OnCheckedChanged(R.id.loop_button)
public void onLoopChanged(boolean loop) {
animationView.loop(loop);
if (!loop) {
animationView.cancelAnimation();
}
}

private void startRecordingDroppedFrames() {
getApplication().startRecordingDroppedFrames();
}

@SuppressLint({"SetTextI18n", "DefaultLocale"})
private void recordDroppedFrames() {
Pair<Integer, Long> droppedFrames = getApplication().stopRecordingDroppedFrames();
int targetFrames = (int) ((droppedFrames.second / 1000000000f) * 60);
int actualFrames = targetFrames - droppedFrames.first;
fpsView.setText(String.format("Fps: %.0f", actualFrames / (animationView.getDuration() / 1000f)));
droppedFramesView.setText("Dropped frames: " + droppedFrames.first);
float droppedFps = droppedFrames.first / (droppedFrames.second / 1000000000f);
droppedFramesPerSecondView.setText(String.format("Dropped frames per second: %.0f", droppedFps));
}

private ILottieApplication getApplication() {
return (ILottieApplication) getActivity().getApplication();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import android.widget.TextView;

import java.io.IOException;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;
Expand All @@ -37,9 +38,8 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);

recyclerView.setAdapter(adapter);
String[] files = null;
try {
adapter.setFiles(getContext().getAssets().list(""));
adapter.setFiles(AssetUtils.getJsonAssets(getContext(), ""));
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directories were appearing in the list and caused a crash when tapped, so this filters out any non-json assets.

} catch (IOException e) {
//noinspection ConstantConditions
Snackbar.make(container, R.string.invalid_assets, Snackbar.LENGTH_LONG).show();
Expand All @@ -66,13 +66,23 @@ private void onGridClicked() {
.commit();
}

private void onCycleClicked() {
getFragmentManager().beginTransaction()
.addToBackStack(null)
.setCustomAnimations(R.anim.slide_in_right, R.anim.hold, R.anim.hold, R.anim.slide_out_right)
.remove(this)
.replace(R.id.content_2, CycleFragment.newInstance())
.commit();
}

final class FileAdapter extends RecyclerView.Adapter<StringViewHolder> {
static final int VIEW_TYPE_GRID = 1;
static final int VIEW_TYPE_FILE = 2;
static final int VIEW_TYPE_CYCLE = 2;
static final int VIEW_TYPE_FILE = 3;

@Nullable private String[] files = null;
@Nullable private List<String> files = null;

void setFiles(@Nullable String[] files) {
void setFiles(@Nullable List<String> files) {
this.files = files;
notifyDataSetChanged();
}
Expand All @@ -84,22 +94,34 @@ public StringViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

@Override
public void onBindViewHolder(StringViewHolder holder, int position) {
if (holder.getItemViewType() == VIEW_TYPE_GRID) {
holder.bind("Grid");
} else {
//noinspection ConstantConditions
holder.bind(files[position - 1]);
switch (holder.getItemViewType()) {
case VIEW_TYPE_GRID:
holder.bind("Grid");
break;
case VIEW_TYPE_CYCLE:
holder.bind("Cycle");
break;
default:
//noinspection ConstantConditions
holder.bind(files.get(position - 2));
}
}

@Override
public int getItemCount() {
return (files == null ? 0 : files.length) + 1;
return (files == null ? 0 : files.size()) + 2;
}

@Override
public int getItemViewType(int position) {
return position == 0 ? VIEW_TYPE_GRID : VIEW_TYPE_FILE;
switch (position) {
case 0:
return VIEW_TYPE_GRID;
case 1:
return VIEW_TYPE_CYCLE;
default:
return VIEW_TYPE_FILE;
}
}
}

Expand All @@ -117,10 +139,15 @@ void bind(final String name) {
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (getItemViewType() == FileAdapter.VIEW_TYPE_GRID) {
onGridClicked();
} else {
onFileClicked(name);
switch (getItemViewType()) {
case FileAdapter.VIEW_TYPE_GRID:
onGridClicked();
break;
case FileAdapter.VIEW_TYPE_CYCLE:
onCycleClicked();
break;
default:
onFileClicked(name);
}
}
});
Expand Down
72 changes: 72 additions & 0 deletions LottieSample/src/main/res/layout/fragment_cycle.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:background="@android:color/white">

<com.airbnb.lottie.LottieAnimationView
android:id="@+id/animation_view"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/outline"/>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

<ToggleButton
android:id="@+id/loop_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOn="Loop"
android:textOff="Single"/>
</LinearLayout>


<android.support.v7.widget.AppCompatSeekBar
android:id="@+id/seek_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"/>
</LinearLayout>

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginLeft="16dp"
android:layout_marginBottom="8dp"
android:orientation="vertical">
<TextView
android:id="@+id/frames_per_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"/>

<TextView
android:id="@+id/dropped_frames"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"/>

<TextView
android:id="@+id/dropped_frames_per_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

</FrameLayout>
Loading