Add Shadow demos to ApiDemos

bug:15724818
Change-Id: I22e3faaee9bb1d46f9c9f16cac38069071e1fc0b
This commit is contained in:
Chris Craik
2014-06-19 10:57:16 -07:00
parent 397b713d36
commit 7007035ef1
7 changed files with 546 additions and 0 deletions

View File

@@ -2685,6 +2685,24 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".graphics.ShadowCardStack"
android:label="Graphics/Shadow Card Stack"
android:theme="@android:style/Theme.Material.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".graphics.ShadowCardDrag"
android:label="Graphics/Shadow Card Drag"
android:theme="@android:style/Theme.Material.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".graphics.WindowSurface" <activity android:name=".graphics.WindowSurface"
android:label="Graphics/Surface Window"> android:label="Graphics/Surface Window">
<intent-filter> <intent-filter>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ffffffff" />
<corners android:radius="10dp" />
</shape>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/card_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/tilt_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/enable_tilt"/>
<CheckBox
android:id="@+id/shading_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/enable_shading"/>
<Button
android:id="@+id/shape_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/select_shape"/>
</LinearLayout>
<TextView
android:id="@+id/card"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="@drawable/round_rect"
android:clipToPadding="false"
android:gravity="center"
android:padding="20dp"
android:text="@string/draggable_card"
android:textSize="20sp"
android:elevation="2dp"/>
</FrameLayout>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/card_parent"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center">
<TextView
android:layout_width="250dp"
android:layout_height="150dp"
android:background="@drawable/round_rect"
android:padding="20dp"
android:textSize="20sp" />
<TextView
android:layout_width="250dp"
android:layout_height="150dp"
android:background="@drawable/round_rect"
android:padding="20dp"
android:textSize="20sp" />
<TextView
android:layout_width="250dp"
android:layout_height="150dp"
android:background="@drawable/round_rect"
android:padding="20dp"
android:textSize="20sp" />
<TextView
android:layout_width="250dp"
android:layout_height="150dp"
android:background="@drawable/round_rect"
android:padding="20dp"
android:textSize="20sp" />
<TextView
android:layout_width="250dp"
android:layout_height="150dp"
android:background="@drawable/round_rect"
android:padding="20dp"
android:textSize="20sp" />
</RelativeLayout>

View File

@@ -904,6 +904,11 @@
<string name="density_title">Density: Unknown Screen</string> <string name="density_title">Density: Unknown Screen</string>
<string name="camera_alert">Device has only one camera!</string> <string name="camera_alert">Device has only one camera!</string>
<string name="switch_cam">Switch Camera</string> <string name="switch_cam">Switch Camera</string>
<string name="draggable_card">Draggable Card</string>
<string name="enable_tilt">Enable Tilt</string>
<string name="enable_shading">Enable Shading</string>
<string name="select_shape">Select Shape</string>
<!-- ============================ --> <!-- ============================ -->
<!-- media examples strings --> <!-- media examples strings -->

View File

@@ -0,0 +1,249 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.apis.graphics;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.graphics.drawable.shapes.RectShape;
import android.graphics.drawable.shapes.RoundRectShape;
import android.graphics.drawable.shapes.Shape;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import com.example.android.apis.R;
import java.util.ArrayList;
public class ShadowCardDrag extends Activity {
private static final float MAX_Z_DP = 10;
private static final float MOMENTUM_SCALE = 10;
private static final int MAX_ANGLE = 10;
private class CardDragState {
long lastEventTime;
float lastX;
float lastY;
float momentumX;
float momentumY;
public void onDown(long eventTime, float x, float y) {
lastEventTime = eventTime;
lastX = x;
lastY = y;
momentumX = 0;
momentumY = 0;
}
public void onMove(long eventTime, float x, float y) {
final long deltaT = eventTime - lastEventTime;
if (deltaT != 0) {
float newMomentumX = (x - lastX) / (mDensity * deltaT);
float newMomentumY = (y - lastY) / (mDensity * deltaT);
momentumX = 0.9f * momentumX + 0.1f * (newMomentumX * MOMENTUM_SCALE);
momentumY = 0.9f * momentumY + 0.1f * (newMomentumY * MOMENTUM_SCALE);
momentumX = Math.max(Math.min((momentumX), MAX_ANGLE), -MAX_ANGLE);
momentumY = Math.max(Math.min((momentumY), MAX_ANGLE), -MAX_ANGLE);
//noinspection SuspiciousNameCombination
mCard.setRotationX(-momentumY);
//noinspection SuspiciousNameCombination
mCard.setRotationY(momentumX);
if (mShadingEnabled) {
float alphaDarkening = (momentumX * momentumX + momentumY * momentumY) / (90 * 90);
alphaDarkening /= 2;
int alphaByte = 0xff - ((int)(alphaDarkening * 255) & 0xff);
int color = Color.rgb(alphaByte, alphaByte, alphaByte);
mCardBackground.setColorFilter(color, PorterDuff.Mode.MULTIPLY);
}
}
lastX = x;
lastY = y;
lastEventTime = eventTime;
}
public void onUp() {
ObjectAnimator flattenX = ObjectAnimator.ofFloat(mCard, "rotationX", 0);
flattenX.setDuration(100);
flattenX.setInterpolator(new AccelerateInterpolator());
flattenX.start();
ObjectAnimator flattenY = ObjectAnimator.ofFloat(mCard, "rotationY", 0);
flattenY.setDuration(100);
flattenY.setInterpolator(new AccelerateInterpolator());
flattenY.start();
mCardBackground.setColorFilter(null);
}
}
/**
* Simple shape example that generates a shadow casting outline.
*/
private static class TriangleShape extends Shape {
private final Path mPath = new Path();
@Override
protected void onResize(float width, float height) {
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(width, 0);
mPath.lineTo(width / 2, height);
mPath.lineTo(0, 0);
mPath.close();
}
@Override
public void draw(Canvas canvas, Paint paint) {
canvas.drawPath(mPath, paint);
}
@Override
public boolean getOutline(Outline outline) {
outline.setConvexPath(mPath);
return true;
}
}
private final ShapeDrawable mCardBackground = new ShapeDrawable();
private final ArrayList<Shape> mShapes = new ArrayList<Shape>();
private float mDensity;
private View mCard;
private final CardDragState mDragState = new CardDragState();
private boolean mTiltEnabled;
private boolean mShadingEnabled;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.shadow_card_drag);
mDensity = getResources().getDisplayMetrics().density;
mShapes.add(new RectShape());
mShapes.add(new OvalShape());
float r = 10 * mDensity;
float radii[] = new float[] {r, r, r, r, r, r, r, r};
mShapes.add(new RoundRectShape(radii, null, null));
mShapes.add(new TriangleShape());
mCardBackground.getPaint().setColor(Color.WHITE);
mCardBackground.setShape(mShapes.get(0));
final View cardParent = findViewById(R.id.card_parent);
mCard = findViewById(R.id.card);
mCard.setBackground(mCardBackground);
final CheckBox tiltCheck = (CheckBox) findViewById(R.id.tilt_check);
tiltCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mTiltEnabled = isChecked;
if (!mTiltEnabled) {
mDragState.onUp();
}
}
});
final CheckBox shadingCheck = (CheckBox) findViewById(R.id.shading_check);
shadingCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mShadingEnabled = isChecked;
if (!mShadingEnabled) {
mCardBackground.setColorFilter(null);
}
}
});
final Button shapeButton = (Button) findViewById(R.id.shape_select);
shapeButton.setOnClickListener(new View.OnClickListener() {
int index = 0;
@Override
public void onClick(View v) {
index = (index + 1) % mShapes.size();
mCardBackground.setShape(mShapes.get(index));
}
});
/**
* Enable any touch on the parent to drag the card. Note that this doesn't do a proper hit
* test, so any drag (including off of the card) will work.
*
* This enables the user to see the effect more clearly for the purpose of this demo.
*/
cardParent.setOnTouchListener(new View.OnTouchListener() {
float downX;
float downY;
long downTime;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX() - mCard.getTranslationX();
downY = event.getY() - mCard.getTranslationY();
downTime = event.getDownTime();
ObjectAnimator upAnim = ObjectAnimator.ofFloat(mCard, "translationZ",
MAX_Z_DP * mDensity);
upAnim.setDuration(100);
upAnim.setInterpolator(new DecelerateInterpolator());
upAnim.start();
if (mTiltEnabled) {
mDragState.onDown(event.getDownTime(), event.getX(), event.getY());
}
break;
case MotionEvent.ACTION_MOVE:
mCard.setTranslationX(event.getX() - downX);
mCard.setTranslationY(event.getY() - downY);
if (mTiltEnabled) {
mDragState.onMove(event.getEventTime(), event.getX(), event.getY());
}
break;
case MotionEvent.ACTION_UP:
ObjectAnimator downAnim = ObjectAnimator.ofFloat(mCard, "translationZ", 0);
downAnim.setDuration(100);
downAnim.setInterpolator(new AccelerateInterpolator());
downAnim.start();
if (mTiltEnabled) {
mDragState.onUp();
}
break;
}
return true;
}
});
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.apis.graphics;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.TextView;
import com.example.android.apis.R;
import java.util.ArrayList;
public class ShadowCardStack extends Activity {
private static final float X_SHIFT_DP = 1000;
private static final float Y_SHIFT_DP = 50;
private static final float Z_LIFT_DP = 8;
private static final float ROTATE_DEGREES = 15;
public AnimatorSet createSet(ArrayList<Animator> items, long startDelay) {
AnimatorSet set = new AnimatorSet();
set.playTogether(items);
set.setStartDelay(startDelay);
return set;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.shadow_card_stack);
float density = getResources().getDisplayMetrics().density;
final ViewGroup cardParent = (ViewGroup) findViewById(R.id.card_parent);
final float X = X_SHIFT_DP * density;
final float Y = Y_SHIFT_DP * density;
final float Z = Z_LIFT_DP * density;
ArrayList<Animator> towardAnimators = new ArrayList<Animator>();
ArrayList<Animator> expandAnimators = new ArrayList<Animator>();
ArrayList<Animator> moveAwayAnimators = new ArrayList<Animator>();
ArrayList<Animator> moveBackAnimators = new ArrayList<Animator>();
ArrayList<Animator> awayAnimators = new ArrayList<Animator>();
ArrayList<Animator> collapseAnimators = new ArrayList<Animator>();
final int max = cardParent.getChildCount();
for (int i = 0; i < max; i++) {
TextView card = (TextView) cardParent.getChildAt(i);
card.setText("Card number " + i);
float targetY = (i - (max-1) / 2.0f) * Y;
Animator expand = ObjectAnimator.ofFloat(card, "translationY", targetY);
expandAnimators.add(expand);
Animator toward = ObjectAnimator.ofFloat(card, "translationZ", i * Z);
toward.setStartDelay(200 * ((max) - i));
towardAnimators.add(toward);
card.setPivotX(X_SHIFT_DP);
Animator rotateAway = ObjectAnimator.ofFloat(card, "rotationY",
i == 0 ? 0 : ROTATE_DEGREES);
rotateAway.setStartDelay(200 * ((max) - i));
rotateAway.setDuration(100);
moveAwayAnimators.add(rotateAway);
Animator slideAway = ObjectAnimator.ofFloat(card, "translationX",
i == 0 ? 0 : X);
slideAway.setStartDelay(200 * ((max) - i));
slideAway.setDuration(100);
moveAwayAnimators.add(slideAway);
Animator rotateBack = ObjectAnimator.ofFloat(card, "rotationY", 0);
rotateBack.setStartDelay(200 * i);
moveBackAnimators.add(rotateBack);
Animator slideBack = ObjectAnimator.ofFloat(card, "translationX", 0);
slideBack.setStartDelay(200 * i);
moveBackAnimators.add(slideBack);
Animator away = ObjectAnimator.ofFloat(card, "translationZ", 0);
away.setStartDelay(200 * i);
awayAnimators.add(away);
Animator collapse = ObjectAnimator.ofFloat(card, "translationY", 0);
collapseAnimators.add(collapse);
}
AnimatorSet totalSet = new AnimatorSet();
totalSet.playSequentially(
createSet(expandAnimators, 250),
createSet(towardAnimators, 0),
createSet(moveAwayAnimators, 250),
createSet(moveBackAnimators, 0),
createSet(awayAnimators, 250),
createSet(collapseAnimators, 0));
totalSet.start();
totalSet.addListener(new RepeatListener(totalSet));
}
public static class RepeatListener implements Animator.AnimatorListener {
final Animator mRepeatAnimator;
public RepeatListener(Animator repeatAnimator) {
mRepeatAnimator = repeatAnimator;
}
@Override
public void onAnimationStart(Animator animation) {}
@Override
public void onAnimationEnd(Animator animation) {
if (animation == mRepeatAnimator) {
mRepeatAnimator.start();
}
}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
}
}