Skip to content

Commit 9f858b0

Browse files
wintmainwosys
authored andcommitted
[wBasis][feat]Add custom transition sample
1 parent ed63cd9 commit 9f858b0

File tree

11 files changed

+644
-0
lines changed

11 files changed

+644
-0
lines changed

app-catalog/samples/wBasis/src/main/AndroidManifest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,11 @@
373373
android:name=".transition.BasicTransitionActivity"
374374
android:exported="true"
375375
android:theme="@style/Theme.AppCompat.DayNight" />
376+
377+
<activity
378+
android:name=".transition.CustomTransitionActivity"
379+
android:exported="true"
380+
android:theme="@style/Theme.AppCompat.DayNight" />
376381
</application>
377382

378383
</manifest>
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2023-2025 wintmain
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.wintmain.wBasis.transition;
18+
19+
import android.os.Bundle;
20+
import android.view.Menu;
21+
import android.view.MenuItem;
22+
import android.widget.TextView;
23+
import android.widget.ViewAnimator;
24+
import androidx.appcompat.app.AppCompatActivity;
25+
import androidx.fragment.app.FragmentTransaction;
26+
import com.google.android.catalog.framework.annotations.Sample;
27+
import com.wintmain.wBasis.R;
28+
import lib.wintmain.wBasis.logger.Log;
29+
import lib.wintmain.wBasis.logger.LogFragment;
30+
import lib.wintmain.wBasis.logger.LogWrapper;
31+
import lib.wintmain.wBasis.logger.MessageOnlyLogFilter;
32+
33+
@Sample(name = "CustomTransition",
34+
description = "自定义的变化",
35+
tags = {"android-samples", "animation-samples"}
36+
)
37+
public class CustomTransitionActivity extends AppCompatActivity {
38+
public static final String TAG = "MainActivity";
39+
40+
// Whether the Log Fragment is currently shown
41+
private boolean mLogShown;
42+
43+
@Override
44+
protected void onCreate(Bundle savedInstanceState) {
45+
super.onCreate(savedInstanceState);
46+
setContentView(R.layout.activity_custom_transition);
47+
48+
if (savedInstanceState == null) {
49+
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
50+
CustomTransitionFragment fragment = new CustomTransitionFragment();
51+
transaction.replace(R.id.sample_content_fragment, fragment);
52+
transaction.commit();
53+
}
54+
}
55+
56+
@Override
57+
protected void onStart() {
58+
super.onStart();
59+
initializeLogging();
60+
}
61+
62+
// Set up targets to receive log data
63+
/** Create a chain of targets that will receive log data */
64+
public void initializeLogging() {
65+
// Wraps Android's native log framework.
66+
LogWrapper logWrapper = new LogWrapper();
67+
// Using Log, front-end to the logging chain, emulates android.util.log method signatures.
68+
Log.setLogNode(logWrapper);
69+
70+
// Filter strips out everything except the message text.
71+
MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
72+
logWrapper.setNext(msgFilter);
73+
74+
// On screen logging via a fragment with a TextView.
75+
LogFragment logFragment = (LogFragment) getSupportFragmentManager()
76+
.findFragmentById(R.id.log_fragment);
77+
msgFilter.setNext(logFragment.getLogView());
78+
79+
Log.i(TAG, "Ready");
80+
}
81+
82+
@Override
83+
public boolean onCreateOptionsMenu(Menu menu) {
84+
getMenuInflater().inflate(R.menu.main, menu);
85+
return true;
86+
}
87+
88+
@Override
89+
public boolean onPrepareOptionsMenu(Menu menu) {
90+
MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
91+
logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
92+
logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
93+
94+
return super.onPrepareOptionsMenu(menu);
95+
}
96+
97+
@Override
98+
public boolean onOptionsItemSelected(MenuItem item) {
99+
if (item.getItemId() == R.id.menu_toggle_log) {
100+
mLogShown = !mLogShown;
101+
ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
102+
if (mLogShown) {
103+
output.setDisplayedChild(1);
104+
} else {
105+
output.setDisplayedChild(0);
106+
}
107+
supportInvalidateOptionsMenu();
108+
return true;
109+
} else if (item.getItemId() == R.id.clear_action) {
110+
TextView tv = (TextView)findViewById(R.id.custom_clear_tv);
111+
tv.setText("");
112+
return true;
113+
}
114+
return super.onOptionsItemSelected(item);
115+
}
116+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2023-2025 wintmain
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.wintmain.wBasis.transition;
18+
19+
import android.content.Context;
20+
import android.os.Bundle;
21+
import android.transition.Scene;
22+
import android.transition.Transition;
23+
import android.transition.TransitionManager;
24+
import android.view.LayoutInflater;
25+
import android.view.View;
26+
import android.view.ViewGroup;
27+
import android.widget.FrameLayout;
28+
import androidx.annotation.NonNull;
29+
import androidx.fragment.app.Fragment;
30+
import com.wintmain.wBasis.R;
31+
import com.wintmain.wBasis.transition.util.ChangeColor;
32+
import lib.wintmain.wBasis.logger.Log;
33+
34+
public class CustomTransitionFragment extends Fragment implements View.OnClickListener {
35+
private static final String TAG = CustomTransitionFragment.class.getSimpleName();
36+
37+
private static final String STATE_CURRENT_SCENE = "current_scene";
38+
39+
/** These are the Scenes we use. */
40+
private Scene[] mScenes;
41+
42+
/** The current index for mScenes. */
43+
private int mCurrentScene;
44+
/** This is the custom Transition we use in this sample. */
45+
private Transition mTransition;
46+
47+
@Override
48+
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
49+
return inflater.inflate(R.layout.fragment_custom_transition, container, false);
50+
}
51+
52+
@Override
53+
public void onViewCreated(View view, Bundle savedInstanceState) {
54+
Context context = getActivity();
55+
FrameLayout container = (FrameLayout) view.findViewById(R.id.container);
56+
view.findViewById(R.id.show_next_scene).setOnClickListener(this);
57+
if (null != savedInstanceState) {
58+
mCurrentScene = savedInstanceState.getInt(STATE_CURRENT_SCENE);
59+
}
60+
// We set up the Scenes here.
61+
mScenes = new Scene[] {
62+
Scene.getSceneForLayout(container, R.layout.custom_scene1, context),
63+
Scene.getSceneForLayout(container, R.layout.custom_scene2, context),
64+
Scene.getSceneForLayout(container, R.layout.custom_scene3, context),
65+
};
66+
// This is the custom Transition.
67+
mTransition = new ChangeColor();
68+
// Show the initial Scene.
69+
TransitionManager.go(mScenes[mCurrentScene % mScenes.length]);
70+
}
71+
72+
@Override
73+
public void onSaveInstanceState(@NonNull Bundle outState) {
74+
super.onSaveInstanceState(outState);
75+
outState.putInt(STATE_CURRENT_SCENE, mCurrentScene);
76+
}
77+
78+
@Override
79+
public void onClick(View v) {
80+
if (v.getId() == R.id.show_next_scene) {
81+
mCurrentScene = (mCurrentScene + 1) % mScenes.length;
82+
Log.i(TAG, "Transitioning to scene #" + mCurrentScene);
83+
// Pass the custom Transition as second argument for TransitionManager.go
84+
TransitionManager.go(mScenes[mCurrentScene], mTransition);
85+
}
86+
}
87+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright 2023-2025 wintmain
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.wintmain.wBasis.transition.util;
18+
19+
import android.animation.Animator;
20+
import android.animation.ArgbEvaluator;
21+
import android.animation.ValueAnimator;
22+
import android.graphics.drawable.ColorDrawable;
23+
import android.graphics.drawable.Drawable;
24+
import android.transition.Transition;
25+
import android.transition.TransitionValues;
26+
import android.view.View;
27+
import android.view.ViewGroup;
28+
import androidx.annotation.NonNull;
29+
30+
public class ChangeColor extends Transition {
31+
/** Key to store a color value in TransitionValues object */
32+
private static final String PROPNAME_BACKGROUND = "customtransition:change_color:background";
33+
34+
@Override
35+
public void captureStartValues(TransitionValues transitionValues) {
36+
captureValues(transitionValues);
37+
}
38+
39+
// Capture the value of the background drawable property for a target in the ending Scene.
40+
@Override
41+
public void captureEndValues(TransitionValues transitionValues) {
42+
captureValues(transitionValues);
43+
}
44+
45+
// BEGIN_INCLUDE (capture_values)
46+
/**
47+
* Convenience method: Add the background Drawable property value
48+
* to the TransitionsValues.value Map for a target.
49+
*/
50+
private void captureValues(TransitionValues values) {
51+
// Capture the property values of views for later use
52+
values.values.put(PROPNAME_BACKGROUND, values.view.getBackground());
53+
}
54+
// END_INCLUDE (capture_values)
55+
56+
// BEGIN_INCLUDE (create_animator)
57+
// Create an animation for each target that is in both the starting and ending Scene. For each
58+
// pair of targets, if their background property value is a color (rather than a graphic),
59+
// create a ValueAnimator based on an ArgbEvaluator that interpolates between the starting and
60+
// ending color. Also create an update listener that sets the View background color for each
61+
// animation frame
62+
@Override
63+
public Animator createAnimator(@NonNull ViewGroup sceneRoot,
64+
TransitionValues startValues, TransitionValues endValues) {
65+
// This transition can only be applied to views that are on both starting and ending scenes.
66+
if (null == startValues || null == endValues) {
67+
return null;
68+
}
69+
// Store a convenient reference to the target. Both the starting and ending layout have the
70+
// same target.
71+
final View view = endValues.view;
72+
// Store the object containing the background property for both the starting and ending
73+
// layouts.
74+
Drawable startBackground = (Drawable) startValues.values.get(PROPNAME_BACKGROUND);
75+
Drawable endBackground = (Drawable) endValues.values.get(PROPNAME_BACKGROUND);
76+
// This transition changes background colors for a target. It doesn't animate any other
77+
// background changes. If the property isn't a ColorDrawable, ignore the target.
78+
if (startBackground instanceof ColorDrawable startColor && endBackground instanceof ColorDrawable endColor) {
79+
// If the background color for the target in the starting and ending layouts is
80+
// different, create an animation.
81+
if (startColor.getColor() != endColor.getColor()) {
82+
// Create a new Animator object to apply to the targets as the transitions framework
83+
// changes from the starting to the ending layout. Use the class ValueAnimator,
84+
// which provides a timing pulse to change property values provided to it. The
85+
// animation runs on the UI thread. The Evaluator controls what type of
86+
// interpolation is done. In this case, an ArgbEvaluator interpolates between two
87+
// #argb values, which are specified as the 2nd and 3rd input arguments.
88+
ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(),
89+
startColor.getColor(), endColor.getColor());
90+
// Add an update listener to the Animator object.
91+
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
92+
@Override
93+
public void onAnimationUpdate(ValueAnimator animation) {
94+
Object value = animation.getAnimatedValue();
95+
// Each time the ValueAnimator produces a new frame in the animation, change
96+
// the background color of the target. Ensure that the value isn't null.
97+
if (null != value) {
98+
view.setBackgroundColor((Integer) value);
99+
}
100+
}
101+
});
102+
// Return the Animator object to the transitions framework. As the framework changes
103+
// between the starting and ending layouts, it applies the animation you've created.
104+
return animator;
105+
}
106+
}
107+
// For non-ColorDrawable backgrounds, we just return null, and no animation will take place.
108+
return null;
109+
}
110+
// END_INCLUDE (create_animator)
111+
}

0 commit comments

Comments
 (0)