upload android base code part7
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.android.insertingcells"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-sdk android:minSdkVersion="14"
|
||||
android:targetSdkVersion="17"/>
|
||||
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
|
||||
<activity android:name=".InsertingCells"
|
||||
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>
|
After Width: | Height: | Size: 96 B |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,53 @@
|
|||
<!-- 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.
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/relative_layout"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".MainActivity"
|
||||
android:background="#111">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linear_layout"
|
||||
android:layout_height="@dimen/cell_height"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.example.android.insertingcells.RoundView
|
||||
android:id="@+id/round_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/add_row_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/add_row"
|
||||
android:onClick="addRow"
|
||||
android:layout_weight="2"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.example.android.insertingcells.InsertionListView
|
||||
android:id="@+id/listview"
|
||||
android:layout_below="@id/linear_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,40 @@
|
|||
<!-- 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"
|
||||
android:id="@+id/item_linear_layout"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/border">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_view"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:scaleType="center"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_view"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="0dp"
|
||||
android:gravity="center"
|
||||
android:layout_weight="2"
|
||||
android:textStyle="bold"
|
||||
android:textSize="22sp"
|
||||
android:textColor="#ffffff"/>
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,21 @@
|
|||
<!-- 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>
|
||||
|
||||
<dimen name="circle_radius">50dp</dimen>
|
||||
<dimen name="cell_height">150dp</dimen>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,21 @@
|
|||
<!-- 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">InsertingCells</string>
|
||||
<string name="add_row">Add Row</string>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* 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.insertingcells;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This custom array adapter is used to populate the ListView in this application.
|
||||
* This adapter also maintains a map of unique stable ids for each object in the data set.
|
||||
* Since this adapter has to support the addition of a new cell to the 1ist index, it also
|
||||
* provides a mechanism to add a stable ID for new data that was recently inserted.
|
||||
*/
|
||||
public class CustomArrayAdapter extends ArrayAdapter<ListItemObject> {
|
||||
|
||||
HashMap<ListItemObject, Integer> mIdMap = new HashMap<ListItemObject, Integer>();
|
||||
List<ListItemObject> mData;
|
||||
Context mContext;
|
||||
int mLayoutViewResourceId;
|
||||
int mCounter;
|
||||
|
||||
public CustomArrayAdapter(Context context, int layoutViewResourceId,
|
||||
List <ListItemObject> data) {
|
||||
super(context, layoutViewResourceId, data);
|
||||
mData = data;
|
||||
mContext = context;
|
||||
mLayoutViewResourceId = layoutViewResourceId;
|
||||
updateStableIds();
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
ListItemObject item = getItem(position);
|
||||
if (mIdMap.containsKey(item)) {
|
||||
return mIdMap.get(item);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void updateStableIds() {
|
||||
mIdMap.clear();
|
||||
mCounter = 0;
|
||||
for (int i = 0; i < mData.size(); ++i) {
|
||||
mIdMap.put(mData.get(i), mCounter++);
|
||||
}
|
||||
}
|
||||
|
||||
public void addStableIdForDataAtPosition(int position) {
|
||||
mIdMap.put(mData.get(position), ++mCounter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
|
||||
ListItemObject obj = mData.get(position);
|
||||
|
||||
if(convertView == null) {
|
||||
LayoutInflater inflater = ((Activity)mContext).getLayoutInflater();
|
||||
convertView = inflater.inflate(mLayoutViewResourceId, parent, false);
|
||||
}
|
||||
|
||||
convertView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams
|
||||
.MATCH_PARENT, obj.getHeight()));
|
||||
|
||||
ImageView imgView = (ImageView)convertView.findViewById(R.id.image_view);
|
||||
TextView textView = (TextView)convertView.findViewById(R.id.text_view);
|
||||
|
||||
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),
|
||||
obj.getImgResource(), null);
|
||||
|
||||
textView.setText(obj.getTitle());
|
||||
imgView.setImageBitmap(CustomArrayAdapter.getCroppedBitmap(bitmap));
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a circular cropped version of the bitmap passed in.
|
||||
*/
|
||||
public static Bitmap getCroppedBitmap(Bitmap bitmap) {
|
||||
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
|
||||
Config.ARGB_8888);
|
||||
|
||||
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
|
||||
Canvas canvas = new Canvas(output);
|
||||
|
||||
final Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
int halfWidth = bitmap.getWidth() / 2;
|
||||
int halfHeight = bitmap.getHeight() / 2;
|
||||
|
||||
canvas.drawCircle(halfWidth, halfHeight, Math.max(halfWidth, halfHeight), paint);
|
||||
|
||||
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
|
||||
|
||||
canvas.drawBitmap(bitmap, rect, rect, paint);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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.insertingcells;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This application creates a ListView to which new elements can be added from the
|
||||
* top. When a new element is added, it is animated from above the bounds
|
||||
* of the list to the top. When the list is scrolled all the way to the top and a new
|
||||
* element is added, the row animation is accompanied by an image animation that pops
|
||||
* out of the round view and pops into the correct position in the top cell.
|
||||
*/
|
||||
public class InsertingCells extends Activity implements OnRowAdditionAnimationListener {
|
||||
|
||||
private ListItemObject mValues[];
|
||||
|
||||
private InsertionListView mListView;
|
||||
|
||||
private Button mButton;
|
||||
|
||||
private Integer mItemNum = 0;
|
||||
|
||||
private RoundView mRoundView;
|
||||
|
||||
private int mCellHeight;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
mValues = new ListItemObject[] {
|
||||
new ListItemObject("Chameleon", R.drawable.chameleon, 0),
|
||||
new ListItemObject("Rock", R.drawable.rock, 0),
|
||||
new ListItemObject("Flower", R.drawable.flower, 0),
|
||||
};
|
||||
|
||||
mCellHeight = (int)(getResources().getDimension(R.dimen.cell_height));
|
||||
|
||||
List<ListItemObject> mData = new ArrayList<ListItemObject>();
|
||||
CustomArrayAdapter mAdapter = new CustomArrayAdapter(this, R.layout.list_view_item, mData);
|
||||
RelativeLayout mLayout = (RelativeLayout)findViewById(R.id.relative_layout);
|
||||
|
||||
mRoundView = (RoundView)findViewById(R.id.round_view);
|
||||
mButton = (Button)findViewById(R.id.add_row_button);
|
||||
mListView = (InsertionListView)findViewById(R.id.listview);
|
||||
|
||||
mListView.setAdapter(mAdapter);
|
||||
mListView.setData(mData);
|
||||
mListView.setLayout(mLayout);
|
||||
mListView.setRowAdditionAnimationListener(this);
|
||||
}
|
||||
|
||||
public void addRow(View view) {
|
||||
mButton.setEnabled(false);
|
||||
|
||||
mItemNum++;
|
||||
ListItemObject obj = mValues[mItemNum % mValues.length];
|
||||
final ListItemObject newObj = new ListItemObject(obj.getTitle(), obj.getImgResource(),
|
||||
mCellHeight);
|
||||
|
||||
boolean shouldAnimateInNewImage = mListView.shouldAnimateInNewImage();
|
||||
if (!shouldAnimateInNewImage) {
|
||||
mListView.addRow(newObj);
|
||||
return;
|
||||
}
|
||||
|
||||
mListView.setEnabled(false);
|
||||
ObjectAnimator animator = mRoundView.getScalingAnimator();
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
mListView.addRow(newObj);
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRowAdditionAnimationStart() {
|
||||
mButton.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRowAdditionAnimationEnd() {
|
||||
mButton.setEnabled(true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* 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.insertingcells;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.TypeEvaluator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This ListView displays a set of ListItemObjects. By calling addRow with a new
|
||||
* ListItemObject, it is added to the top of the ListView and the new row is animated
|
||||
* in. If the ListView content is at the top (the scroll offset is 0), the animation of
|
||||
* the new row is accompanied by an extra image animation that pops into place in its
|
||||
* corresponding item in the ListView.
|
||||
*/
|
||||
public class InsertionListView extends ListView {
|
||||
|
||||
private static final int NEW_ROW_DURATION = 500;
|
||||
private static final int OVERSHOOT_INTERPOLATOR_TENSION = 5;
|
||||
|
||||
private OvershootInterpolator sOvershootInterpolator;
|
||||
|
||||
private RelativeLayout mLayout;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private OnRowAdditionAnimationListener mRowAdditionAnimationListener;
|
||||
|
||||
private List<ListItemObject> mData;
|
||||
private List<BitmapDrawable> mCellBitmapDrawables;
|
||||
|
||||
public InsertionListView(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public InsertionListView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public InsertionListView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public void init(Context context) {
|
||||
setDivider(null);
|
||||
mContext = context;
|
||||
mCellBitmapDrawables = new ArrayList<BitmapDrawable>();
|
||||
sOvershootInterpolator = new OvershootInterpolator(OVERSHOOT_INTERPOLATOR_TENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the underlying data set and adapter through the addition of the new object
|
||||
* to the first item of the ListView. The new cell is then animated into place from
|
||||
* above the bounds of the ListView.
|
||||
*/
|
||||
public void addRow(ListItemObject newObj) {
|
||||
|
||||
final CustomArrayAdapter adapter = (CustomArrayAdapter)getAdapter();
|
||||
|
||||
/**
|
||||
* Stores the starting bounds and the corresponding bitmap drawables of every
|
||||
* cell present in the ListView before the data set change takes place.
|
||||
*/
|
||||
final HashMap<Long, Rect> listViewItemBounds = new HashMap<Long, Rect>();
|
||||
final HashMap<Long, BitmapDrawable> listViewItemDrawables = new HashMap<Long,
|
||||
BitmapDrawable>();
|
||||
|
||||
int firstVisiblePosition = getFirstVisiblePosition();
|
||||
for (int i = 0; i < getChildCount(); ++i) {
|
||||
View child = getChildAt(i);
|
||||
int position = firstVisiblePosition + i;
|
||||
long itemID = adapter.getItemId(position);
|
||||
Rect startRect = new Rect(child.getLeft(), child.getTop(), child.getRight(),
|
||||
child.getBottom());
|
||||
listViewItemBounds.put(itemID, startRect);
|
||||
listViewItemDrawables.put(itemID, getBitmapDrawableFromView(child));
|
||||
}
|
||||
|
||||
/** Adds the new object to the data set, thereby modifying the adapter,
|
||||
* as well as adding a stable Id for that specified object.*/
|
||||
mData.add(0, newObj);
|
||||
adapter.addStableIdForDataAtPosition(0);
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
final ViewTreeObserver observer = getViewTreeObserver();
|
||||
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
|
||||
public boolean onPreDraw() {
|
||||
observer.removeOnPreDrawListener(this);
|
||||
|
||||
ArrayList<Animator> animations = new ArrayList<Animator>();
|
||||
|
||||
final View newCell = getChildAt(0);
|
||||
final ImageView imgView = (ImageView)newCell.findViewById(R.id.image_view);
|
||||
final ImageView copyImgView = new ImageView(mContext);
|
||||
|
||||
int firstVisiblePosition = getFirstVisiblePosition();
|
||||
final boolean shouldAnimateInNewRow = shouldAnimateInNewRow();
|
||||
final boolean shouldAnimateInImage = shouldAnimateInNewImage();
|
||||
|
||||
if (shouldAnimateInNewRow) {
|
||||
/** Fades in the text of the first cell. */
|
||||
TextView textView = (TextView)newCell.findViewById(R.id.text_view);
|
||||
ObjectAnimator textAlphaAnimator = ObjectAnimator.ofFloat(textView,
|
||||
View.ALPHA, 0.0f, 1.0f);
|
||||
animations.add(textAlphaAnimator);
|
||||
|
||||
/** Animates in the extra hover view corresponding to the image
|
||||
* in the top row of the ListView. */
|
||||
if (shouldAnimateInImage) {
|
||||
|
||||
int width = imgView.getWidth();
|
||||
int height = imgView.getHeight();
|
||||
|
||||
Point childLoc = getLocationOnScreen(newCell);
|
||||
Point layoutLoc = getLocationOnScreen(mLayout);
|
||||
|
||||
ListItemObject obj = mData.get(0);
|
||||
Bitmap bitmap = CustomArrayAdapter.getCroppedBitmap(BitmapFactory
|
||||
.decodeResource(mContext.getResources(), obj.getImgResource(),
|
||||
null));
|
||||
copyImgView.setImageBitmap(bitmap);
|
||||
|
||||
imgView.setVisibility(View.INVISIBLE);
|
||||
|
||||
copyImgView.setScaleType(ImageView.ScaleType.CENTER);
|
||||
|
||||
ObjectAnimator imgViewTranslation = ObjectAnimator.ofFloat(copyImgView,
|
||||
View.Y, childLoc.y - layoutLoc.y);
|
||||
|
||||
PropertyValuesHolder imgViewScaleY = PropertyValuesHolder.ofFloat
|
||||
(View.SCALE_Y, 0, 1.0f);
|
||||
PropertyValuesHolder imgViewScaleX = PropertyValuesHolder.ofFloat
|
||||
(View.SCALE_X, 0, 1.0f);
|
||||
ObjectAnimator imgViewScaleAnimator = ObjectAnimator
|
||||
.ofPropertyValuesHolder(copyImgView, imgViewScaleX, imgViewScaleY);
|
||||
imgViewScaleAnimator.setInterpolator(sOvershootInterpolator);
|
||||
animations.add(imgViewTranslation);
|
||||
animations.add(imgViewScaleAnimator);
|
||||
|
||||
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams
|
||||
(width, height);
|
||||
|
||||
mLayout.addView(copyImgView, params);
|
||||
}
|
||||
}
|
||||
|
||||
/** Loops through all the current visible cells in the ListView and animates
|
||||
* all of them into their post layout positions from their original positions.*/
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
int position = firstVisiblePosition + i;
|
||||
long itemId = adapter.getItemId(position);
|
||||
Rect startRect = listViewItemBounds.get(itemId);
|
||||
int top = child.getTop();
|
||||
if (startRect != null) {
|
||||
/** If the cell was visible before the data set change and
|
||||
* after the data set change, then animate the cell between
|
||||
* the two positions.*/
|
||||
int startTop = startRect.top;
|
||||
int delta = startTop - top;
|
||||
ObjectAnimator animation = ObjectAnimator.ofFloat(child,
|
||||
View.TRANSLATION_Y, delta, 0);
|
||||
animations.add(animation);
|
||||
} else {
|
||||
/** If the cell was not visible (or present) before the data set
|
||||
* change but is visible after the data set change, then use its
|
||||
* height to determine the delta by which it should be animated.*/
|
||||
int childHeight = child.getHeight() + getDividerHeight();
|
||||
int startTop = top + (i > 0 ? childHeight : -childHeight);
|
||||
int delta = startTop - top;
|
||||
ObjectAnimator animation = ObjectAnimator.ofFloat(child,
|
||||
View.TRANSLATION_Y, delta, 0);
|
||||
animations.add(animation);
|
||||
}
|
||||
listViewItemBounds.remove(itemId);
|
||||
listViewItemDrawables.remove(itemId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through all the cells that were visible before the data set
|
||||
* changed but not after, and keeps track of their corresponding
|
||||
* drawables. The bounds of each drawable are then animated from the
|
||||
* original state to the new one (off the screen). By storing all
|
||||
* the drawables that meet this criteria, they can be redrawn on top
|
||||
* of the ListView via dispatchDraw as they are animating.
|
||||
*/
|
||||
for (Long itemId: listViewItemBounds.keySet()) {
|
||||
BitmapDrawable bitmapDrawable = listViewItemDrawables.get(itemId);
|
||||
Rect startBounds = listViewItemBounds.get(itemId);
|
||||
bitmapDrawable.setBounds(startBounds);
|
||||
|
||||
int childHeight = startBounds.bottom - startBounds.top + getDividerHeight();
|
||||
Rect endBounds = new Rect(startBounds);
|
||||
endBounds.offset(0, childHeight);
|
||||
|
||||
ObjectAnimator animation = ObjectAnimator.ofObject(bitmapDrawable,
|
||||
"bounds", sBoundsEvaluator, startBounds, endBounds);
|
||||
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
private Rect mLastBound = null;
|
||||
private Rect mCurrentBound = new Rect();
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator valueAnimator) {
|
||||
Rect bounds = (Rect)valueAnimator.getAnimatedValue();
|
||||
mCurrentBound.set(bounds);
|
||||
if (mLastBound != null) {
|
||||
mCurrentBound.union(mLastBound);
|
||||
}
|
||||
mLastBound = bounds;
|
||||
invalidate(mCurrentBound);
|
||||
}
|
||||
});
|
||||
|
||||
listViewItemBounds.remove(itemId);
|
||||
listViewItemDrawables.remove(itemId);
|
||||
|
||||
mCellBitmapDrawables.add(bitmapDrawable);
|
||||
|
||||
animations.add(animation);
|
||||
}
|
||||
|
||||
/** Animates all the cells from their old position to their new position
|
||||
* at the same time.*/
|
||||
setEnabled(false);
|
||||
mRowAdditionAnimationListener.onRowAdditionAnimationStart();
|
||||
AnimatorSet set = new AnimatorSet();
|
||||
set.setDuration(NEW_ROW_DURATION);
|
||||
set.playTogether(animations);
|
||||
set.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mCellBitmapDrawables.clear();
|
||||
imgView.setVisibility(View.VISIBLE);
|
||||
mLayout.removeView(copyImgView);
|
||||
mRowAdditionAnimationListener.onRowAdditionAnimationEnd();
|
||||
setEnabled(true);
|
||||
invalidate();
|
||||
}
|
||||
});
|
||||
set.start();
|
||||
|
||||
listViewItemBounds.clear();
|
||||
listViewItemDrawables.clear();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* By overriding dispatchDraw, the BitmapDrawables of all the cells that were on the
|
||||
* screen before (but not after) the layout are drawn and animated off the screen.
|
||||
*/
|
||||
@Override
|
||||
protected void dispatchDraw (Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
if (mCellBitmapDrawables.size() > 0) {
|
||||
for (BitmapDrawable bitmapDrawable: mCellBitmapDrawables) {
|
||||
bitmapDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldAnimateInNewRow() {
|
||||
int firstVisiblePosition = getFirstVisiblePosition();
|
||||
return (firstVisiblePosition == 0);
|
||||
}
|
||||
|
||||
public boolean shouldAnimateInNewImage() {
|
||||
if (getChildCount() == 0) {
|
||||
return true;
|
||||
}
|
||||
boolean shouldAnimateInNewRow = shouldAnimateInNewRow();
|
||||
View topCell = getChildAt(0);
|
||||
return (shouldAnimateInNewRow && topCell.getTop() == 0);
|
||||
}
|
||||
|
||||
/** Returns a bitmap drawable showing a screenshot of the view passed in. */
|
||||
private BitmapDrawable getBitmapDrawableFromView(View v) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas (bitmap);
|
||||
v.draw(canvas);
|
||||
return new BitmapDrawable(getResources(), bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute x,y coordinates of the view relative to the top left
|
||||
* corner of the phone screen.
|
||||
*/
|
||||
public Point getLocationOnScreen(View v) {
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||
|
||||
int[] location = new int[2];
|
||||
v.getLocationOnScreen(location);
|
||||
|
||||
return new Point(location[0], location[1]);
|
||||
}
|
||||
|
||||
/** Setter for the underlying data set controlling the adapter. */
|
||||
public void setData(List<ListItemObject> data) {
|
||||
mData = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the parent RelativeLayout of this ListView. A reference to this
|
||||
* ViewGroup is required in order to add the custom animated overlaying bitmap
|
||||
* when adding a new row.
|
||||
*/
|
||||
public void setLayout(RelativeLayout layout) {
|
||||
mLayout = layout;
|
||||
}
|
||||
|
||||
public void setRowAdditionAnimationListener(OnRowAdditionAnimationListener
|
||||
rowAdditionAnimationListener) {
|
||||
mRowAdditionAnimationListener = rowAdditionAnimationListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* This TypeEvaluator is used to animate the position of a BitmapDrawable
|
||||
* by updating its bounds.
|
||||
*/
|
||||
static final TypeEvaluator<Rect> sBoundsEvaluator = new TypeEvaluator<Rect>() {
|
||||
public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
|
||||
return new Rect(interpolate(startValue.left, endValue.left, fraction),
|
||||
interpolate(startValue.top, endValue.top, fraction),
|
||||
interpolate(startValue.right, endValue.right, fraction),
|
||||
interpolate(startValue.bottom, endValue.bottom, fraction));
|
||||
}
|
||||
|
||||
public int interpolate(int start, int end, float fraction) {
|
||||
return (int)(start + fraction * (end - start));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.insertingcells;
|
||||
|
||||
/**
|
||||
* The data model for every cell in the ListView for this application. This model stores
|
||||
* a title, an image resource and a default cell height for every item in the ListView.
|
||||
*/
|
||||
public class ListItemObject {
|
||||
|
||||
private String mTitle;
|
||||
private int mImgResource;
|
||||
private int mHeight;
|
||||
|
||||
public ListItemObject(String title, int imgResource, int height) {
|
||||
super();
|
||||
mTitle = title;
|
||||
mImgResource = imgResource;
|
||||
mHeight = height;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public int getImgResource() {
|
||||
return mImgResource;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.insertingcells;
|
||||
|
||||
/**
|
||||
* This listener is used to determine when the animation of a new row addition
|
||||
* begins and ends. The primary use of this interface is to create a callback
|
||||
* under which certain elements, such as the listview itself, can be disabled
|
||||
* to prevent unpredictable behaviour during the actual cell animation.
|
||||
*/
|
||||
public interface OnRowAdditionAnimationListener {
|
||||
public void onRowAdditionAnimationStart();
|
||||
public void onRowAdditionAnimationEnd();
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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.insertingcells;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* This round view draws a circle from which the image pops out of and into
|
||||
* the corresponding cell in the list.
|
||||
*/
|
||||
public class RoundView extends View {
|
||||
|
||||
private final int STROKE_WIDTH = 6;
|
||||
private final int RADIUS = 20;
|
||||
private final int ANIMATION_DURATION = 300;
|
||||
private final float SCALE_FACTOR = 0.3f;
|
||||
|
||||
private Paint mPaint;
|
||||
|
||||
public RoundView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public RoundView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public RoundView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mPaint = new Paint();
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setColor(Color.WHITE);
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setStrokeWidth(STROKE_WIDTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight() / 2,
|
||||
RADIUS, mPaint);
|
||||
}
|
||||
|
||||
public ObjectAnimator getScalingAnimator() {
|
||||
PropertyValuesHolder imgViewScaleY = PropertyValuesHolder.ofFloat(View
|
||||
.SCALE_Y, SCALE_FACTOR);
|
||||
PropertyValuesHolder imgViewScaleX = PropertyValuesHolder.ofFloat(View
|
||||
.SCALE_X, SCALE_FACTOR);
|
||||
|
||||
ObjectAnimator imgViewScaleAnimator = ObjectAnimator
|
||||
.ofPropertyValuesHolder(this, imgViewScaleX, imgViewScaleY);
|
||||
imgViewScaleAnimator.setRepeatCount(1);
|
||||
imgViewScaleAnimator.setRepeatMode(ValueAnimator.REVERSE);
|
||||
imgViewScaleAnimator.setDuration(ANIMATION_DURATION);
|
||||
|
||||
return imgViewScaleAnimator;
|
||||
}
|
||||
}
|