Merge "First commit for FoldingLayout."
This commit is contained in:
19
samples/devbytes/graphics/FoldingLayout/AndroidManifest.xml
Normal file
19
samples/devbytes/graphics/FoldingLayout/AndroidManifest.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.example.android.foldinglayout"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0">
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-feature android:name="android.hardware.camera" />
|
||||||
|
<uses-sdk android:minSdkVersion="17"
|
||||||
|
android:targetSdkVersion="18"/>
|
||||||
|
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
|
||||||
|
<activity android:name=".FoldingLayoutActivity"
|
||||||
|
android:label="@string/app_name">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 240 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,41 @@
|
|||||||
|
<!-- Copyright (C) 2013 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.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.example.android.foldinglayout.FoldingLayout
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:id="@+id/fold_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_view"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:scaleType="fitXY"/>
|
||||||
|
|
||||||
|
</com.example.android.foldinglayout.FoldingLayout>
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/anchor_seek_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:max="100"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<!-- Copyright (C) 2013 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.
|
||||||
|
-->
|
||||||
|
<Spinner xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:entries="@array/num_of_folds_array" />
|
||||||
41
samples/devbytes/graphics/FoldingLayout/res/menu/fold.xml
Normal file
41
samples/devbytes/graphics/FoldingLayout/res/menu/fold.xml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<!-- Copyright (C) 2013 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.
|
||||||
|
-->
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/toggle_orientation"
|
||||||
|
android:showAsAction="never"
|
||||||
|
android:title="@string/vertical"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/num_of_folds"
|
||||||
|
android:showAsAction="ifRoom"
|
||||||
|
android:actionLayout="@layout/spinner"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:title="@string/sepia_effect_off"
|
||||||
|
android:id="@+id/sepia"
|
||||||
|
android:checkable="true"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:title="@string/camera_feed"
|
||||||
|
android:id="@+id/camera_feed"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/animate_fold"
|
||||||
|
android:showAsAction="never"
|
||||||
|
android:title="@string/animate"/>
|
||||||
|
|
||||||
|
</menu>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<!-- Copyright (C) 2013 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.
|
||||||
|
-->
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/toggle_orientation"
|
||||||
|
android:showAsAction="never"
|
||||||
|
android:title="@string/vertical"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/num_of_folds"
|
||||||
|
android:showAsAction="ifRoom"
|
||||||
|
android:actionLayout="@layout/spinner"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/animate_fold"
|
||||||
|
android:showAsAction="never"
|
||||||
|
android:title="@string/animate"/>
|
||||||
|
|
||||||
|
</menu>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<!-- Copyright (C) 2013 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.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">FoldingLayout</string>
|
||||||
|
<string name="title_activity_fold">FoldActivity</string>
|
||||||
|
|
||||||
|
<string name="horizontal">Horizontal</string>
|
||||||
|
<string name="vertical">Vertical</string>
|
||||||
|
<string name="num_of_folds">Number Of Folds</string>
|
||||||
|
|
||||||
|
<string name="animate">Animate</string>
|
||||||
|
|
||||||
|
<string name="camera_feed">Camera Feed</string>
|
||||||
|
<string name="static_image">Static Image</string>
|
||||||
|
|
||||||
|
<string name="sepia_effect_off">Sepia Off</string>
|
||||||
|
|
||||||
|
<string-array name="num_of_folds_array">
|
||||||
|
<item>2</item>
|
||||||
|
<item>3</item>
|
||||||
|
<item>4</item>
|
||||||
|
<item>5</item>
|
||||||
|
<item>6</item>
|
||||||
|
<item>7</item>
|
||||||
|
<item>8</item>
|
||||||
|
<item>1</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,545 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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.foldinglayout;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.LinearGradient;
|
||||||
|
import android.graphics.Matrix;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Paint.Style;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Shader.TileMode;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The folding layout where the number of folds, the anchor point and the
|
||||||
|
* orientation of the fold can be specified. Each of these parameters can
|
||||||
|
* be modified individually and updates and resets the fold to a default
|
||||||
|
* (unfolded) state. The fold factor varies between 0 (completely unfolded
|
||||||
|
* flat image) to 1.0 (completely folded, non-visible image).
|
||||||
|
*
|
||||||
|
* This layout throws an exception if there is more than one child added to the view.
|
||||||
|
* For more complicated view hierarchy's inside the folding layout, the views should all
|
||||||
|
* be nested inside 1 parent layout.
|
||||||
|
*
|
||||||
|
* This layout folds the contents of its child in real time. By applying matrix
|
||||||
|
* transformations when drawing to canvas, the contents of the child may change as
|
||||||
|
* the fold takes place. It is important to note that there are jagged edges about
|
||||||
|
* the perimeter of the layout as a result of applying transformations to a rectangle.
|
||||||
|
* This can be avoided by having the child of this layout wrap its content inside a
|
||||||
|
* 1 pixel transparent border. This will cause an anti-aliasing like effect and smoothen
|
||||||
|
* out the edges.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FoldingLayout extends ViewGroup {
|
||||||
|
|
||||||
|
public static enum Orientation {
|
||||||
|
VERTICAL,
|
||||||
|
HORIZONTAL
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String FOLDING_VIEW_EXCEPTION_MESSAGE = "Folding Layout can only 1 child at " +
|
||||||
|
"most";
|
||||||
|
|
||||||
|
private final float SHADING_ALPHA = 0.8f;
|
||||||
|
private final float SHADING_FACTOR = 0.5f;
|
||||||
|
private final int DEPTH_CONSTANT = 1500;
|
||||||
|
private final int NUM_OF_POLY_POINTS = 8;
|
||||||
|
|
||||||
|
private Rect[] mFoldRectArray;
|
||||||
|
|
||||||
|
private Matrix [] mMatrix;
|
||||||
|
|
||||||
|
private Orientation mOrientation = Orientation.HORIZONTAL;
|
||||||
|
|
||||||
|
private float mAnchorFactor = 0;
|
||||||
|
private float mFoldFactor = 0;
|
||||||
|
|
||||||
|
private int mNumberOfFolds = 2;
|
||||||
|
|
||||||
|
private boolean mIsHorizontal = true;
|
||||||
|
|
||||||
|
private int mOriginalWidth = 0;
|
||||||
|
private int mOriginalHeight = 0;
|
||||||
|
|
||||||
|
private float mFoldMaxWidth = 0;
|
||||||
|
private float mFoldMaxHeight = 0;
|
||||||
|
private float mFoldDrawWidth = 0;
|
||||||
|
private float mFoldDrawHeight = 0;
|
||||||
|
|
||||||
|
private boolean mIsFoldPrepared = false;
|
||||||
|
private boolean mShouldDraw = true;
|
||||||
|
|
||||||
|
private Paint mSolidShadow;
|
||||||
|
private Paint mGradientShadow;
|
||||||
|
private LinearGradient mShadowLinearGradient;
|
||||||
|
private Matrix mShadowGradientMatrix;
|
||||||
|
|
||||||
|
private float [] mSrc;
|
||||||
|
private float [] mDst;
|
||||||
|
|
||||||
|
private OnFoldListener mFoldListener;
|
||||||
|
|
||||||
|
private float mPreviousFoldFactor = 0;
|
||||||
|
|
||||||
|
private Bitmap mFullBitmap;
|
||||||
|
private Rect mDstRect;
|
||||||
|
|
||||||
|
public FoldingLayout(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FoldingLayout(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FoldingLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean addViewInLayout(View child, int index, LayoutParams params,
|
||||||
|
boolean preventRequestLayout) {
|
||||||
|
throwCustomException(getChildCount());
|
||||||
|
boolean returnValue = super.addViewInLayout(child, index, params, preventRequestLayout);
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addView(View child, int index, LayoutParams params) {
|
||||||
|
throwCustomException(getChildCount());
|
||||||
|
super.addView(child, index, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
View child = getChildAt(0);
|
||||||
|
measureChild(child,widthMeasureSpec, heightMeasureSpec);
|
||||||
|
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
|
View child = getChildAt(0);
|
||||||
|
child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
|
||||||
|
updateFold();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The custom exception to be thrown so as to limit the number of views in this
|
||||||
|
* layout to at most one.
|
||||||
|
*/
|
||||||
|
private class NumberOfFoldingLayoutChildrenException extends RuntimeException {
|
||||||
|
public NumberOfFoldingLayoutChildrenException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Throws an exception if the number of views added to this layout exceeds one.*/
|
||||||
|
private void throwCustomException (int numOfChildViews) {
|
||||||
|
if (numOfChildViews == 1) {
|
||||||
|
throw new NumberOfFoldingLayoutChildrenException(FOLDING_VIEW_EXCEPTION_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFoldListener(OnFoldListener foldListener) {
|
||||||
|
mFoldListener = foldListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the fold factor of the folding view and updates all the corresponding
|
||||||
|
* matrices and values to account for the new fold factor. Once that is complete,
|
||||||
|
* it redraws itself with the new fold. */
|
||||||
|
public void setFoldFactor(float foldFactor) {
|
||||||
|
if (foldFactor != mFoldFactor) {
|
||||||
|
mFoldFactor = foldFactor;
|
||||||
|
calculateMatrices();
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrientation(Orientation orientation) {
|
||||||
|
if (orientation != mOrientation) {
|
||||||
|
mOrientation = orientation;
|
||||||
|
updateFold();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAnchorFactor(float anchorFactor) {
|
||||||
|
if (anchorFactor != mAnchorFactor) {
|
||||||
|
mAnchorFactor = anchorFactor;
|
||||||
|
updateFold();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumberOfFolds(int numberOfFolds) {
|
||||||
|
if (numberOfFolds != mNumberOfFolds) {
|
||||||
|
mNumberOfFolds = numberOfFolds;
|
||||||
|
updateFold();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getAnchorFactor() {
|
||||||
|
return mAnchorFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Orientation getOrientation() {
|
||||||
|
return mOrientation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getFoldFactor() {
|
||||||
|
return mFoldFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumberOfFolds() {
|
||||||
|
return mNumberOfFolds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFold() {
|
||||||
|
prepareFold(mOrientation, mAnchorFactor, mNumberOfFolds);
|
||||||
|
calculateMatrices();
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called in order to update the fold's orientation, anchor
|
||||||
|
* point and number of folds. This creates the necessary setup in order to
|
||||||
|
* prepare the layout for a fold with the specified parameters. Some of the
|
||||||
|
* dimensions required for the folding transformation are also acquired here.
|
||||||
|
*
|
||||||
|
* After this method is called, it will be in a completely unfolded state by default.
|
||||||
|
*/
|
||||||
|
private void prepareFold(Orientation orientation, float anchorFactor, int numberOfFolds) {
|
||||||
|
|
||||||
|
mSrc = new float[NUM_OF_POLY_POINTS];
|
||||||
|
mDst = new float[NUM_OF_POLY_POINTS];
|
||||||
|
|
||||||
|
mDstRect = new Rect();
|
||||||
|
|
||||||
|
mFoldFactor = 0;
|
||||||
|
mPreviousFoldFactor = 0;
|
||||||
|
|
||||||
|
mIsFoldPrepared = false;
|
||||||
|
|
||||||
|
mSolidShadow = new Paint();
|
||||||
|
mGradientShadow = new Paint();
|
||||||
|
|
||||||
|
mOrientation = orientation;
|
||||||
|
mIsHorizontal = (orientation == Orientation.HORIZONTAL);
|
||||||
|
|
||||||
|
if (mIsHorizontal) {
|
||||||
|
mShadowLinearGradient = new LinearGradient(0, 0, SHADING_FACTOR, 0, Color.BLACK,
|
||||||
|
Color.TRANSPARENT, TileMode.CLAMP);
|
||||||
|
} else {
|
||||||
|
mShadowLinearGradient = new LinearGradient(0, 0, 0, SHADING_FACTOR, Color.BLACK,
|
||||||
|
Color.TRANSPARENT, TileMode.CLAMP);
|
||||||
|
}
|
||||||
|
|
||||||
|
mGradientShadow.setStyle(Style.FILL);
|
||||||
|
mGradientShadow.setShader(mShadowLinearGradient);
|
||||||
|
mShadowGradientMatrix = new Matrix();
|
||||||
|
|
||||||
|
mAnchorFactor = anchorFactor;
|
||||||
|
mNumberOfFolds = numberOfFolds;
|
||||||
|
|
||||||
|
mOriginalWidth = getMeasuredWidth();
|
||||||
|
mOriginalHeight = getMeasuredHeight();
|
||||||
|
|
||||||
|
mFoldRectArray = new Rect[mNumberOfFolds];
|
||||||
|
mMatrix = new Matrix [mNumberOfFolds];
|
||||||
|
|
||||||
|
for (int x = 0; x < mNumberOfFolds; x++) {
|
||||||
|
mMatrix[x] = new Matrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
int h = mOriginalHeight;
|
||||||
|
int w = mOriginalWidth;
|
||||||
|
|
||||||
|
if (FoldingLayoutActivity.IS_JBMR2) {
|
||||||
|
mFullBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(mFullBitmap);
|
||||||
|
getChildAt(0).draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
int delta = Math.round(mIsHorizontal ? ((float) w) / ((float) mNumberOfFolds) :
|
||||||
|
((float) h) /((float) mNumberOfFolds));
|
||||||
|
|
||||||
|
/* Loops through the number of folds and segments the full layout into a number
|
||||||
|
* of smaller equal components. If the number of folds is odd, then one of the
|
||||||
|
* components will be smaller than all the rest. Note that deltap below handles
|
||||||
|
* the calculation for an odd number of folds.*/
|
||||||
|
for (int x = 0; x < mNumberOfFolds; x++) {
|
||||||
|
if (mIsHorizontal) {
|
||||||
|
int deltap = (x + 1) * delta > w ? w - x * delta : delta;
|
||||||
|
mFoldRectArray[x] = new Rect(x * delta, 0, x * delta + deltap, h);
|
||||||
|
} else {
|
||||||
|
int deltap = (x + 1) * delta > h ? h - x * delta : delta;
|
||||||
|
mFoldRectArray[x] = new Rect(0, x * delta, w, x * delta + deltap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIsHorizontal) {
|
||||||
|
mFoldMaxHeight = h;
|
||||||
|
mFoldMaxWidth = delta;
|
||||||
|
} else {
|
||||||
|
mFoldMaxHeight = delta;
|
||||||
|
mFoldMaxWidth = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsFoldPrepared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculates the transformation matrices used to draw each of the separate folding
|
||||||
|
* segments from this view.
|
||||||
|
*/
|
||||||
|
private void calculateMatrices() {
|
||||||
|
|
||||||
|
mShouldDraw = true;
|
||||||
|
|
||||||
|
if (!mIsFoldPrepared) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** If the fold factor is 1 than the folding view should not be seen
|
||||||
|
* and the canvas can be left completely empty. */
|
||||||
|
if (mFoldFactor == 1) {
|
||||||
|
mShouldDraw = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mFoldFactor == 0 && mPreviousFoldFactor > 0) {
|
||||||
|
mFoldListener.onEndFold();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPreviousFoldFactor == 0 && mFoldFactor > 0) {
|
||||||
|
mFoldListener.onStartFold();
|
||||||
|
}
|
||||||
|
|
||||||
|
mPreviousFoldFactor = mFoldFactor;
|
||||||
|
|
||||||
|
/* Reset all the transformation matrices back to identity before computing
|
||||||
|
* the new transformation */
|
||||||
|
for (int x = 0; x < mNumberOfFolds; x++) {
|
||||||
|
mMatrix[x].reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
float cTranslationFactor = 1 - mFoldFactor;
|
||||||
|
|
||||||
|
float translatedDistance = mIsHorizontal ? mOriginalWidth * cTranslationFactor :
|
||||||
|
mOriginalHeight * cTranslationFactor;
|
||||||
|
|
||||||
|
float translatedDistancePerFold = Math.round(translatedDistance / mNumberOfFolds);
|
||||||
|
|
||||||
|
/* For an odd number of folds, the rounding error may cause the
|
||||||
|
* translatedDistancePerFold to be grater than the max fold width or height. */
|
||||||
|
mFoldDrawWidth = mFoldMaxWidth < translatedDistancePerFold ?
|
||||||
|
translatedDistancePerFold : mFoldMaxWidth;
|
||||||
|
mFoldDrawHeight = mFoldMaxHeight < translatedDistancePerFold ?
|
||||||
|
translatedDistancePerFold : mFoldMaxHeight;
|
||||||
|
|
||||||
|
float translatedDistanceFoldSquared = translatedDistancePerFold * translatedDistancePerFold;
|
||||||
|
|
||||||
|
/* Calculate the depth of the fold into the screen using pythagorean theorem. */
|
||||||
|
float depth = mIsHorizontal ?
|
||||||
|
(float)Math.sqrt((double)(mFoldDrawWidth * mFoldDrawWidth -
|
||||||
|
translatedDistanceFoldSquared)) :
|
||||||
|
(float)Math.sqrt((double)(mFoldDrawHeight * mFoldDrawHeight -
|
||||||
|
translatedDistanceFoldSquared));
|
||||||
|
|
||||||
|
/* The size of some object is always inversely proportional to the distance
|
||||||
|
* it is away from the viewpoint. The constant can be varied to to affect the
|
||||||
|
* amount of perspective. */
|
||||||
|
float scaleFactor = DEPTH_CONSTANT / (DEPTH_CONSTANT + depth);
|
||||||
|
|
||||||
|
float scaledWidth, scaledHeight, bottomScaledPoint, topScaledPoint, rightScaledPoint,
|
||||||
|
leftScaledPoint;
|
||||||
|
|
||||||
|
if (mIsHorizontal) {
|
||||||
|
scaledWidth = mFoldDrawWidth * cTranslationFactor;
|
||||||
|
scaledHeight = mFoldDrawHeight * scaleFactor;
|
||||||
|
} else {
|
||||||
|
scaledWidth = mFoldDrawWidth * scaleFactor;
|
||||||
|
scaledHeight = mFoldDrawHeight * cTranslationFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
topScaledPoint = (mFoldDrawHeight - scaledHeight) / 2.0f;
|
||||||
|
bottomScaledPoint = topScaledPoint + scaledHeight;
|
||||||
|
|
||||||
|
leftScaledPoint = (mFoldDrawWidth - scaledWidth) / 2.0f;
|
||||||
|
rightScaledPoint = leftScaledPoint + scaledWidth;
|
||||||
|
|
||||||
|
float anchorPoint = mIsHorizontal ? mAnchorFactor * mOriginalWidth :
|
||||||
|
mAnchorFactor * mOriginalHeight;
|
||||||
|
|
||||||
|
/* The fold along which the anchor point is located. */
|
||||||
|
float midFold = mIsHorizontal ? (anchorPoint / mFoldDrawWidth) : anchorPoint /
|
||||||
|
mFoldDrawHeight;
|
||||||
|
|
||||||
|
mSrc[0] = 0;
|
||||||
|
mSrc[1] = 0;
|
||||||
|
mSrc[2] = 0;
|
||||||
|
mSrc[3] = mFoldDrawHeight;
|
||||||
|
mSrc[4] = mFoldDrawWidth;
|
||||||
|
mSrc[5] = 0;
|
||||||
|
mSrc[6] = mFoldDrawWidth;
|
||||||
|
mSrc[7] = mFoldDrawHeight;
|
||||||
|
|
||||||
|
/* Computes the transformation matrix for each fold using the values calculated above. */
|
||||||
|
for (int x = 0; x < mNumberOfFolds; x++) {
|
||||||
|
|
||||||
|
boolean isEven = (x % 2 == 0);
|
||||||
|
|
||||||
|
if (mIsHorizontal) {
|
||||||
|
mDst[0] = (anchorPoint > x * mFoldDrawWidth) ? anchorPoint + (x - midFold) *
|
||||||
|
scaledWidth : anchorPoint - (midFold - x) * scaledWidth;
|
||||||
|
mDst[1] = isEven ? 0 : topScaledPoint;
|
||||||
|
mDst[2] = mDst[0];
|
||||||
|
mDst[3] = isEven ? mFoldDrawHeight: bottomScaledPoint;
|
||||||
|
mDst[4] = (anchorPoint > (x + 1) * mFoldDrawWidth) ? anchorPoint + (x + 1 - midFold)
|
||||||
|
* scaledWidth : anchorPoint - (midFold - x - 1) * scaledWidth;
|
||||||
|
mDst[5] = isEven ? topScaledPoint : 0;
|
||||||
|
mDst[6] = mDst[4];
|
||||||
|
mDst[7] = isEven ? bottomScaledPoint : mFoldDrawHeight;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
mDst[0] = isEven ? 0 : leftScaledPoint;
|
||||||
|
mDst[1] = (anchorPoint > x * mFoldDrawHeight) ? anchorPoint + (x - midFold) *
|
||||||
|
scaledHeight : anchorPoint - (midFold - x) * scaledHeight;
|
||||||
|
mDst[2] = isEven ? leftScaledPoint: 0;
|
||||||
|
mDst[3] = (anchorPoint > (x + 1) * mFoldDrawHeight) ? anchorPoint + (x + 1 -
|
||||||
|
midFold) * scaledHeight : anchorPoint - (midFold - x - 1) * scaledHeight;
|
||||||
|
mDst[4] = isEven ? mFoldDrawWidth : rightScaledPoint;
|
||||||
|
mDst[5] = mDst[1];
|
||||||
|
mDst[6] = isEven ? rightScaledPoint : mFoldDrawWidth;
|
||||||
|
mDst[7] = mDst[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pixel fractions are present for odd number of folds which need to be
|
||||||
|
* rounded off here.*/
|
||||||
|
for (int y = 0; y < 8; y ++) {
|
||||||
|
mDst[y] = Math.round(mDst[y]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If it so happens that any of the folds have reached a point where
|
||||||
|
* the width or height of that fold is 0, then nothing needs to be
|
||||||
|
* drawn onto the canvas because the view is essentially completely
|
||||||
|
* folded.*/
|
||||||
|
if (mIsHorizontal) {
|
||||||
|
if (mDst[4] <= mDst[0] || mDst[6] <= mDst[2]) {
|
||||||
|
mShouldDraw = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mDst[3] <= mDst[1] || mDst[7] <= mDst[5]) {
|
||||||
|
mShouldDraw = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets the shadow and bitmap transformation matrices.*/
|
||||||
|
mMatrix[x].setPolyToPoly(mSrc, 0, mDst, 0, NUM_OF_POLY_POINTS / 2);
|
||||||
|
}
|
||||||
|
/* The shadows on the folds are split into two parts: Solid shadows and gradients.
|
||||||
|
* Every other fold has a solid shadow which overlays the whole fold. Similarly,
|
||||||
|
* the folds in between these alternating folds also have an overlaying shadow.
|
||||||
|
* However, it is a gradient that takes up part of the fold as opposed to a solid
|
||||||
|
* shadow overlaying the whole fold.*/
|
||||||
|
|
||||||
|
/* Solid shadow paint object. */
|
||||||
|
int alpha = (int) (mFoldFactor * 255 * SHADING_ALPHA);
|
||||||
|
|
||||||
|
mSolidShadow.setColor(Color.argb(alpha, 0, 0, 0));
|
||||||
|
|
||||||
|
if (mIsHorizontal) {
|
||||||
|
mShadowGradientMatrix.setScale(mFoldDrawWidth, 1);
|
||||||
|
mShadowLinearGradient.setLocalMatrix(mShadowGradientMatrix);
|
||||||
|
} else {
|
||||||
|
mShadowGradientMatrix.setScale(1, mFoldDrawHeight);
|
||||||
|
mShadowLinearGradient.setLocalMatrix(mShadowGradientMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
mGradientShadow.setAlpha(alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dispatchDraw(Canvas canvas) {
|
||||||
|
/** If prepareFold has not been called or if preparation has not completed yet,
|
||||||
|
* then no custom drawing will take place so only need to invoke super's
|
||||||
|
* onDraw and return. */
|
||||||
|
if (!mIsFoldPrepared || mFoldFactor == 0) {
|
||||||
|
super.dispatchDraw(canvas);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mShouldDraw) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect src;
|
||||||
|
/* Draws the bitmaps and shadows on the canvas with the appropriate transformations. */
|
||||||
|
for (int x = 0; x < mNumberOfFolds; x++) {
|
||||||
|
|
||||||
|
src = mFoldRectArray[x];
|
||||||
|
/* The canvas is saved and restored for every individual fold*/
|
||||||
|
canvas.save();
|
||||||
|
|
||||||
|
/* Concatenates the canvas with the transformation matrix for the
|
||||||
|
* the segment of the view corresponding to the actual image being
|
||||||
|
* displayed. */
|
||||||
|
canvas.concat(mMatrix[x]);
|
||||||
|
if (FoldingLayoutActivity.IS_JBMR2) {
|
||||||
|
mDstRect.set(0, 0, src.width(), src.height());
|
||||||
|
canvas.drawBitmap(mFullBitmap, src, mDstRect, null);
|
||||||
|
} else {
|
||||||
|
/* The same transformation matrix is used for both the shadow and the image
|
||||||
|
* segment. The canvas is clipped to account for the size of each fold and
|
||||||
|
* is translated so they are drawn in the right place. The shadow is then drawn on
|
||||||
|
* top of the different folds using the sametransformation matrix.*/
|
||||||
|
canvas.clipRect(0, 0, src.right - src.left, src.bottom - src.top);
|
||||||
|
|
||||||
|
if (mIsHorizontal) {
|
||||||
|
canvas.translate(-src.left, 0);
|
||||||
|
} else {
|
||||||
|
canvas.translate(0, -src.top);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.dispatchDraw(canvas);
|
||||||
|
|
||||||
|
if (mIsHorizontal) {
|
||||||
|
canvas.translate(src.left, 0);
|
||||||
|
} else {
|
||||||
|
canvas.translate(0, src.top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Draws the shadows corresponding to this specific fold. */
|
||||||
|
if (x % 2 == 0) {
|
||||||
|
canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight, mSolidShadow);
|
||||||
|
} else {
|
||||||
|
canvas.drawRect(0, 0, mFoldDrawWidth, mFoldDrawHeight, mGradientShadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,429 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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.foldinglayout;
|
||||||
|
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.ColorMatrix;
|
||||||
|
import android.graphics.ColorMatrixColorFilter;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.SurfaceTexture;
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.GestureDetector;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.TextureView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewConfiguration;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.animation.AccelerateInterpolator;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
|
||||||
|
import com.example.android.foldinglayout.FoldingLayout.Orientation;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This application creates a paper like folding effect of some view.
|
||||||
|
* The number of folds, orientation (vertical or horizontal) of the fold, and the
|
||||||
|
* anchor point about which the view will fold can be set to achieve different
|
||||||
|
* folding effects.
|
||||||
|
*
|
||||||
|
* Using bitmap and canvas scaling techniques, the foldingLayout can be scaled so as
|
||||||
|
* to depict a paper-like folding effect. The addition of shadows on the separate folds
|
||||||
|
* adds a sense of realism to the visual effect.
|
||||||
|
*
|
||||||
|
* This application shows folding of a TextureView containing a live camera feed,
|
||||||
|
* as well as the folding of an ImageView with a static image. The TextureView experiences
|
||||||
|
* jagged edges as a result of scaling operations on rectangles. The ImageView however
|
||||||
|
* contains a 1 pixel transparent border around its contents which can be used to avoid
|
||||||
|
* this unwanted artifact.
|
||||||
|
*/
|
||||||
|
public class FoldingLayoutActivity extends Activity {
|
||||||
|
|
||||||
|
private final int ANTIALIAS_PADDING = 1;
|
||||||
|
|
||||||
|
private final int FOLD_ANIMATION_DURATION = 1000;
|
||||||
|
|
||||||
|
/* A bug was introduced in Android 4.3 that ignores changes to the Canvas state
|
||||||
|
* between multiple calls to super.dispatchDraw() when running with hardware acceleration.
|
||||||
|
* To account for this bug, a slightly different approach was taken to fold a
|
||||||
|
* static image whereby a bitmap of the original contents is captured and drawn
|
||||||
|
* in segments onto the canvas. However, this method does not permit the folding
|
||||||
|
* of a TextureView hosting a live camera feed which continuously updates.
|
||||||
|
* Furthermore, the sepia effect was removed from the bitmap variation of the
|
||||||
|
* demo to simplify the logic when running with this workaround."
|
||||||
|
*/
|
||||||
|
static final boolean IS_JBMR2 = Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR2;
|
||||||
|
|
||||||
|
private FoldingLayout mFoldLayout;
|
||||||
|
private SeekBar mAnchorSeekBar;
|
||||||
|
private Orientation mOrientation = Orientation.HORIZONTAL;
|
||||||
|
|
||||||
|
private int mTranslation = 0;
|
||||||
|
private int mNumberOfFolds = 2;
|
||||||
|
private int mParentPositionY = -1;
|
||||||
|
private int mTouchSlop = -1;
|
||||||
|
|
||||||
|
private float mAnchorFactor = 0;
|
||||||
|
|
||||||
|
private boolean mDidLoadSpinner = true;
|
||||||
|
private boolean mDidNotStartScroll = true;
|
||||||
|
|
||||||
|
private boolean mIsCameraFeed = false;
|
||||||
|
private boolean mIsSepiaOn = true;
|
||||||
|
|
||||||
|
private GestureDetector mScrollGestureDetector;
|
||||||
|
private ItemSelectedListener mItemSelectedListener;
|
||||||
|
|
||||||
|
private Camera mCamera;
|
||||||
|
private TextureView mTextureView;
|
||||||
|
private ImageView mImageView;
|
||||||
|
|
||||||
|
private Paint mSepiaPaint;
|
||||||
|
private Paint mDefaultPaint;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_fold);
|
||||||
|
|
||||||
|
mImageView = (ImageView)findViewById(R.id.image_view);
|
||||||
|
mImageView.setPadding(ANTIALIAS_PADDING, ANTIALIAS_PADDING, ANTIALIAS_PADDING,
|
||||||
|
ANTIALIAS_PADDING);
|
||||||
|
mImageView.setScaleType(ImageView.ScaleType.FIT_XY);
|
||||||
|
mImageView.setImageDrawable(getResources().getDrawable(R.drawable.image));
|
||||||
|
|
||||||
|
mTextureView = new TextureView(this);
|
||||||
|
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
|
||||||
|
|
||||||
|
mAnchorSeekBar = (SeekBar)findViewById(R.id.anchor_seek_bar);
|
||||||
|
mFoldLayout = (FoldingLayout)findViewById(R.id.fold_view);
|
||||||
|
mFoldLayout.setBackgroundColor(Color.BLACK);
|
||||||
|
mFoldLayout.setFoldListener(mOnFoldListener);
|
||||||
|
|
||||||
|
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
|
||||||
|
|
||||||
|
mAnchorSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener);
|
||||||
|
|
||||||
|
mScrollGestureDetector = new GestureDetector(this, new ScrollGestureDetector());
|
||||||
|
mItemSelectedListener = new ItemSelectedListener();
|
||||||
|
|
||||||
|
mDefaultPaint = new Paint();
|
||||||
|
mSepiaPaint = new Paint();
|
||||||
|
|
||||||
|
ColorMatrix m1 = new ColorMatrix();
|
||||||
|
ColorMatrix m2 = new ColorMatrix();
|
||||||
|
m1.setSaturation(0);
|
||||||
|
m2.setScale(1f, .95f, .82f, 1.0f);
|
||||||
|
m1.setConcat(m2, m1);
|
||||||
|
mSepiaPaint.setColorFilter(new ColorMatrixColorFilter(m1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener, along with the setSepiaLayer method below, show a possible use case
|
||||||
|
* of the OnFoldListener provided with the FoldingLayout. This is a fun extra addition
|
||||||
|
* to the demo showing what kind of visual effects can be applied to the child of the
|
||||||
|
* FoldingLayout by setting the layer type to hardware. With a hardware layer type
|
||||||
|
* applied to the child, a paint object can also be applied to the same layer. Using
|
||||||
|
* the concatenation of two different color matrices (above), a color filter was created
|
||||||
|
* which simulates a sepia effect on the layer.*/
|
||||||
|
private OnFoldListener mOnFoldListener =
|
||||||
|
new OnFoldListener() {
|
||||||
|
@Override
|
||||||
|
public void onStartFold() {
|
||||||
|
if (mIsSepiaOn) {
|
||||||
|
setSepiaLayer(mFoldLayout.getChildAt(0), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEndFold() {
|
||||||
|
setSepiaLayer(mFoldLayout.getChildAt(0), false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void setSepiaLayer (View view, boolean isSepiaLayerOn) {
|
||||||
|
if (!IS_JBMR2) {
|
||||||
|
if (isSepiaLayerOn) {
|
||||||
|
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||||
|
view.setLayerPaint(mSepiaPaint);
|
||||||
|
} else {
|
||||||
|
view.setLayerPaint(mDefaultPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a SurfaceTextureListener in order to prepare a TextureView
|
||||||
|
* which displays a live, and continuously updated, feed from the Camera.
|
||||||
|
*/
|
||||||
|
private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView
|
||||||
|
.SurfaceTextureListener() {
|
||||||
|
@Override
|
||||||
|
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i2) {
|
||||||
|
mCamera = Camera.open();
|
||||||
|
|
||||||
|
if (mCamera == null && Camera.getNumberOfCameras() > 1) {
|
||||||
|
mCamera = mCamera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCamera == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
mCamera.setPreviewTexture(surfaceTexture);
|
||||||
|
mCamera.setDisplayOrientation(90);
|
||||||
|
mCamera.startPreview();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i2) {
|
||||||
|
// Ignored, Camera does all the work for us
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
|
||||||
|
if (mCamera != null) {
|
||||||
|
mCamera.stopPreview();
|
||||||
|
mCamera.release();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
|
||||||
|
// Invoked every time there's a new Camera preview frame
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A listener for scrolling changes in the seekbar. The anchor point of the folding
|
||||||
|
* view is updated every time the seekbar stops tracking touch events. Every time the
|
||||||
|
* anchor point is updated, the folding view is restored to a default unfolded state.
|
||||||
|
*/
|
||||||
|
private SeekBar.OnSeekBarChangeListener mSeekBarChangeListener = new SeekBar
|
||||||
|
.OnSeekBarChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
|
mTranslation = 0;
|
||||||
|
mAnchorFactor = ((float)mAnchorSeekBar.getProgress())/100.0f;
|
||||||
|
mFoldLayout.setAnchorFactor(mAnchorFactor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
if (IS_JBMR2) {
|
||||||
|
getMenuInflater().inflate(R.menu.fold_with_bug, menu);
|
||||||
|
} else {
|
||||||
|
getMenuInflater().inflate(R.menu.fold, menu);
|
||||||
|
}
|
||||||
|
Spinner s = (Spinner) menu.findItem(R.id.num_of_folds).getActionView();
|
||||||
|
s.setOnItemSelectedListener(mItemSelectedListener);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWindowFocusChanged (boolean hasFocus) {
|
||||||
|
super.onWindowFocusChanged(hasFocus);
|
||||||
|
|
||||||
|
int[] loc = new int[2];
|
||||||
|
mFoldLayout.getLocationOnScreen(loc);
|
||||||
|
mParentPositionY = loc[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent me) {
|
||||||
|
return mScrollGestureDetector.onTouchEvent(me);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected (MenuItem item) {
|
||||||
|
switch(item.getItemId()) {
|
||||||
|
case R.id.animate_fold:
|
||||||
|
animateFold();
|
||||||
|
break;
|
||||||
|
case R.id.toggle_orientation:
|
||||||
|
mOrientation = (mOrientation == Orientation.HORIZONTAL) ? Orientation.VERTICAL :
|
||||||
|
Orientation.HORIZONTAL;
|
||||||
|
item.setTitle((mOrientation == Orientation.HORIZONTAL) ? R.string.vertical :
|
||||||
|
R.string.horizontal);
|
||||||
|
mTranslation = 0;
|
||||||
|
mFoldLayout.setOrientation(mOrientation);
|
||||||
|
break;
|
||||||
|
case R.id.camera_feed:
|
||||||
|
mIsCameraFeed = !mIsCameraFeed;
|
||||||
|
item.setTitle(mIsCameraFeed ? R.string.static_image : R.string.camera_feed);
|
||||||
|
item.setChecked(mIsCameraFeed);
|
||||||
|
if (mIsCameraFeed) {
|
||||||
|
mFoldLayout.removeView(mImageView);
|
||||||
|
mFoldLayout.addView(mTextureView, new ViewGroup.LayoutParams(
|
||||||
|
mFoldLayout.getWidth(), mFoldLayout.getHeight()));
|
||||||
|
} else {
|
||||||
|
mFoldLayout.removeView(mTextureView);
|
||||||
|
mFoldLayout.addView(mImageView, new ViewGroup.LayoutParams(
|
||||||
|
mFoldLayout.getWidth(), mFoldLayout.getHeight()));
|
||||||
|
}
|
||||||
|
mTranslation = 0;
|
||||||
|
break;
|
||||||
|
case R.id.sepia:
|
||||||
|
mIsSepiaOn = !mIsSepiaOn;
|
||||||
|
item.setChecked(!mIsSepiaOn);
|
||||||
|
if (mIsSepiaOn && mFoldLayout.getFoldFactor() != 0) {
|
||||||
|
setSepiaLayer(mFoldLayout.getChildAt(0), true);
|
||||||
|
} else {
|
||||||
|
setSepiaLayer(mFoldLayout.getChildAt(0), false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animates the folding view inwards (to a completely folded state) from its
|
||||||
|
* current state and then back out to its original state.
|
||||||
|
*/
|
||||||
|
public void animateFold ()
|
||||||
|
{
|
||||||
|
float foldFactor = mFoldLayout.getFoldFactor();
|
||||||
|
|
||||||
|
ObjectAnimator animator = ObjectAnimator.ofFloat(mFoldLayout, "foldFactor", foldFactor, 1);
|
||||||
|
animator.setRepeatMode(ValueAnimator.REVERSE);
|
||||||
|
animator.setRepeatCount(1);
|
||||||
|
animator.setDuration(FOLD_ANIMATION_DURATION);
|
||||||
|
animator.setInterpolator(new AccelerateInterpolator());
|
||||||
|
animator.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for selection events of the spinner located on the action bar. Every
|
||||||
|
* time a new value is selected, the number of folds in the folding view is updated
|
||||||
|
* and is also restored to a default unfolded state.
|
||||||
|
*/
|
||||||
|
private class ItemSelectedListener implements OnItemSelectedListener {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||||
|
mNumberOfFolds = Integer.parseInt(parent.getItemAtPosition(pos).toString());
|
||||||
|
if (mDidLoadSpinner) {
|
||||||
|
mDidLoadSpinner = false;
|
||||||
|
} else {
|
||||||
|
mTranslation = 0;
|
||||||
|
mFoldLayout.setNumberOfFolds(mNumberOfFolds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> arg0) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This class uses user touch events to fold and unfold the folding view. */
|
||||||
|
private class ScrollGestureDetector extends GestureDetector.SimpleOnGestureListener {
|
||||||
|
@Override
|
||||||
|
public boolean onDown (MotionEvent e) {
|
||||||
|
mDidNotStartScroll = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All the logic here is used to determine by what factor the paper view should
|
||||||
|
* be folded in response to the user's touch events. The logic here uses vertical
|
||||||
|
* scrolling to fold a vertically oriented view and horizontal scrolling to fold
|
||||||
|
* a horizontally oriented fold. Depending on where the anchor point of the fold is,
|
||||||
|
* movements towards or away from the anchor point will either fold or unfold
|
||||||
|
* the paper respectively.
|
||||||
|
*
|
||||||
|
* The translation logic here also accounts for the touch slop when a new user touch
|
||||||
|
* begins, but before a scroll event is first invoked.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||||
|
int touchSlop = 0;
|
||||||
|
float factor;
|
||||||
|
if (mOrientation == Orientation.VERTICAL) {
|
||||||
|
factor = Math.abs((float)(mTranslation) / (float)(mFoldLayout.getHeight()));
|
||||||
|
|
||||||
|
if (e2.getY() - mParentPositionY <= mFoldLayout.getHeight()
|
||||||
|
&& e2.getY() - mParentPositionY >= 0) {
|
||||||
|
if ((e2.getY() - mParentPositionY) > mFoldLayout.getHeight() * mAnchorFactor) {
|
||||||
|
mTranslation -= (int)distanceY;
|
||||||
|
touchSlop = distanceY < 0 ? -mTouchSlop : mTouchSlop;
|
||||||
|
} else {
|
||||||
|
mTranslation += (int)distanceY;
|
||||||
|
touchSlop = distanceY < 0 ? mTouchSlop : -mTouchSlop;
|
||||||
|
}
|
||||||
|
mTranslation = mDidNotStartScroll ? mTranslation + touchSlop : mTranslation;
|
||||||
|
|
||||||
|
if (mTranslation < -mFoldLayout.getHeight()) {
|
||||||
|
mTranslation = -mFoldLayout.getHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
factor = Math.abs(((float)mTranslation) / ((float) mFoldLayout.getWidth()));
|
||||||
|
|
||||||
|
if (e2.getRawX() > mFoldLayout.getWidth() * mAnchorFactor) {
|
||||||
|
mTranslation -= (int)distanceX;
|
||||||
|
touchSlop = distanceX < 0 ? -mTouchSlop : mTouchSlop;
|
||||||
|
} else {
|
||||||
|
mTranslation += (int)distanceX;
|
||||||
|
touchSlop = distanceX < 0 ? mTouchSlop : -mTouchSlop;
|
||||||
|
}
|
||||||
|
mTranslation = mDidNotStartScroll ? mTranslation + touchSlop : mTranslation;
|
||||||
|
|
||||||
|
if (mTranslation < -mFoldLayout.getWidth()) {
|
||||||
|
mTranslation = -mFoldLayout.getWidth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mDidNotStartScroll = false;
|
||||||
|
|
||||||
|
if (mTranslation > 0) {
|
||||||
|
mTranslation = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mFoldLayout.setFoldFactor(factor);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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.foldinglayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface listens for when the folding layout begins folding (enters
|
||||||
|
* a folded state from a completely unfolded state), or ends folding (enters a
|
||||||
|
* completely unfolded state from a folded state).
|
||||||
|
*/
|
||||||
|
public interface OnFoldListener {
|
||||||
|
public void onStartFold();
|
||||||
|
public void onEndFold();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user