最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Android酷炫动画效果之3D星体旋转效果
时间:2022-06-25 22:58:35 编辑:袖梨 来源:一聚教程网
在Android中,如果想要实现3D动画效果一般有两种选择:一是使用Open GL ES,二是使用Camera。Open GL ES使用起来太过复杂,一般是用于比较高级的3D特效或游戏,并且这个也不是开源的,像比较简单的一些3D效果,使用Camera就足够了。
一些熟知的Android 3D动画如对某个View进行旋转或翻转的 Rotate3dAnimation类,还有使用Gallery( Gallery目前已过时,现在都推荐使用 HorizontalScrollView或 RecyclerView替代其实现相应功能) 实现的3D画廊效果等,当然有一些特效要通过伪3D变换来实现,比如CoverFlow效果,它使用标准Android 2D库,还是继承的Gallery类并自定义一些方法,具体实现和使用请参照。
本文要实现的3D星体旋转效果也是从这个CoverFlow演绎而来,不过CoverFlow只是对图像进行转动,我这里要实现的效果是要对所有的View进行类似旋转木马的转动,并且CoverFlow还存在很多已知bug,所以我这里需要重写一些类,并且将Scroller类用Rotator类替代,使界面看起来具有滚动效果,实际上是在转动一组图像。
首先我们需要自定义控件的一些属性,我们将控件取名Carousel,需要设置子项的最小个数和最大个数、当前选中项以及定义旋转角度等,attrs.xml
The CarouselImageView Class
这个类装载控件子项在3D空间的位置、子项的索引和当前子项的角度,通过实现Comparable接口,帮助我们确定子项绘制的顺序
package com.john.carousel.lib; import android.content.Context; import android.util.AttributeSet; import android.widget.ImageView; public class CarouselImageView extends ImageView implements Comparable{ private int index; private float currentAngle; private float x; private float y; private float z; private boolean drawn; public CarouselImageView(Context context) { this(context, null, 0); } public CarouselImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CarouselImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setIndex(int index) { this.index = index; } public int getIndex() { return index; } public void setCurrentAngle(float currentAngle) { this.currentAngle = currentAngle; } public float getCurrentAngle() { return currentAngle; } public int compareTo(CarouselImageView another) { return (int) (another.z - this.z); } public void setX(float x) { this.x = x; } public float getX() { return x; } public void setY(float y) { this.y = y; } public float getY() { return y; } public void setZ(float z) { this.z = z; } public float getZ() { return z; } public void setDrawn(boolean drawn) { this.drawn = drawn; } public boolean isDrawn() { return drawn; } }
The Carousel Item Class
这个类简化我上面定义的 CarouselImageView一些控件属性
package com.john.carousel.lib; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Matrix; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; public class CarouselItem extends FrameLayout implements Comparable{ public ImageView mImage; public TextView mText, mTextUp; public Context context; public int index; public float currentAngle; public float itemX; public float itemY; public float itemZ; public float degX; public float degY; public float degZ; public boolean drawn; // It's needed to find screen coordinates private Matrix mCIMatrix; public CarouselItem(Context context) { super(context); this.context = context; FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); this.setLayoutParams(params); LayoutInflater inflater = LayoutInflater.from(context); View itemTemplate = inflater.inflate(R.layout.carousel_item, this, true); mImage = (ImageView) itemTemplate.findViewById(R.id.item_image); mText = (TextView) itemTemplate.findViewById(R.id.item_text); mTextUp = (TextView) itemTemplate.findViewById(R.id.item_text_up); } public void setTextColor(int i) { this.mText.setTextColor(context.getResources().getColorStateList(i)); this.mTextUp.setTextColor(context.getResources().getColorStateList(i)); } public String getName() { return mText.getText().toString(); } public void setIndex(int index) { this.index = index; } public int getIndex() { return index; } public void setCurrentAngle(float currentAngle) { if (index == 0 && currentAngle > 5) { Log.d("", ""); } this.currentAngle = currentAngle; } public float getCurrentAngle() { return currentAngle; } public int compareTo(CarouselItem another) { return (int) (another.itemZ - this.itemZ); } public void setItemX(float x) { this.itemX = x; } public float getItemX() { return itemX; } public void setItemY(float y) { this.itemY = y; } public float getItemY() { return itemY; } public void setItemZ(float z) { this.itemZ = z; } public float getItemZ() { return itemZ; } public float getDegX() { return degX; } public void setDegX(float degX) { this.degX = degX; } public float getDegY() { return degY; } public void setDegY(float degY) { this.degY = degY; } public float getDegZ() { return degZ; } public void setDegZ(float degZ) { this.degZ = degZ; } public void setDrawn(boolean drawn) { this.drawn = drawn; } public boolean isDrawn() { return drawn; } public void setImageBitmap(Bitmap bitmap) { mImage.setImageBitmap(bitmap); } public void setText(int i) { String s = context.getResources().getString(i); mText.setText(s); mTextUp.setText(s); } public void setText(String txt) { mText.setText(txt); mTextUp.setText(txt); } Matrix getCIMatrix() { return mCIMatrix; } void setCIMatrix(Matrix mMatrix) { this.mCIMatrix = mMatrix; } public void setImage(int i) { mImage.setImageDrawable(context.getResources().getDrawable(i)); } public void setVisiblity(int id) { if (id == 0) { mText.setVisibility(View.INVISIBLE); mTextUp.setVisibility(View.VISIBLE); } else { mTextUp.setVisibility(View.INVISIBLE); mText.setVisibility(View.VISIBLE); } } }
The Rotator Class
如果你去查看Scroller类方法,你会发现它定义了两种操作模式:滑动模式和抛动作,用来计算当前相对于给出的起始位置的偏移量,我们需要移除一些不需要的成员变量,添加我们自己的成员,并且修改相应的计算方法
package com.john.carousel.lib; import android.content.Context; import android.view.animation.AnimationUtils; /** * This class encapsulates rotation. The duration of the rotation can be passed * in the constructor and specifies the maximum time that the rotation animation * should take. Past this time, the rotation is automatically moved to its final * stage and computeRotationOffset() will always return false to indicate that * scrolling is over. */ public class Rotator { private float mStartAngle; private float mCurrAngle; private long mStartTime; private long mDuration; private float mDeltaAngle; private boolean mFinished; private int direction; private float mCurrDeg; public Rotator(Context context) { mFinished = true; } public final boolean isFinished() { return mFinished; } /** * Force the finished field to a particular value. * * @param finished * The new finished value. */ public final void forceFinished(boolean finished) { mFinished = finished; } /** * Returns how long the scroll event will take, in milliseconds. * * @return The duration of the scroll in milliseconds. */ public final long getDuration() { return mDuration; } /** * Returns the current X offset in the scroll. * * @return The new X offset as an absolute distance from the origin. */ public final float getCurrAngle() { return mCurrAngle; } public final float getStartAngle() { return mStartAngle; } /** * Returns the time elapsed since the beginning of the scrolling. * * @return The elapsed time in milliseconds. */ public int timePassed() { return (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime); } public int getdirection() { return this.direction; } public float getCurrDeg() { return this.mCurrDeg; } /** * Extend the scroll animation. */ public void extendDuration(int extend) { int passed = timePassed(); mDuration = passed + extend; mFinished = false; } /** * Stops the animation. Contrary to {@link #forceFinished(boolean)}, * aborting the animating cause the scroller to move to the final x and y * position * * @see #forceFinished(boolean) */ public void abortAnimation() { mFinished = true; } /** * Call this when you want to know the new location. If it returns true, the * animation is not yet finished. loc will be altered to provide the new * location. */ public boolean computeAngleOffset() { if (mFinished) { return false; } long systemClock = AnimationUtils.currentAnimationTimeMillis(); long timePassed = systemClock - mStartTime; if (timePassed
The CarouselSpinner Class
package com.john.carousel.lib; import android.content.Context; import android.database.DataSetObserver; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; import android.widget.AbsSpinner; import android.widget.SpinnerAdapter; public abstract class CarouselSpinner extends CarouselAdapter{ SpinnerAdapter mAdapter; int mHeightMeasureSpec; int mWidthMeasureSpec; boolean mBlockLayoutRequests; int mSelectionLeftPadding = 0; int mSelectionTopPadding = 0; int mSelectionRightPadding = 0; int mSelectionBottomPadding = 0; final Rect mSpinnerPadding = new Rect(); final RecycleBin mRecycler = new RecycleBin(); private DataSetObserver mDataSetObserver; public CarouselSpinner(Context context) { super(context); initCarouselSpinner(); } public CarouselSpinner(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CarouselSpinner(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initCarouselSpinner(); } /** * Common code for different constructor flavors */ private void initCarouselSpinner() { setFocusable(true); setWillNotDraw(false); } @Override public SpinnerAdapter getAdapter() { return mAdapter; } @Override public void setAdapter(SpinnerAdapter adapter) { if (null != mAdapter) { mAdapter.unregisterDataSetObserver(mDataSetObserver); resetList(); } mAdapter = adapter; mOldSelectedPosition = INVALID_POSITION; mOldSelectedRowId = INVALID_ROW_ID; if (mAdapter != null) { mOldItemCount = mItemCount; mItemCount = mAdapter.getCount(); checkFocus(); mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver); int position = mItemCount > 0 ? 0 : INVALID_POSITION; setSelectedPositionInt(position); setNextSelectedPositionInt(position); if (mItemCount == 0) { // Nothing selected checkSelectionChanged(); } } else { checkFocus(); resetList(); // Nothing selected checkSelectionChanged(); } requestLayout(); } @Override public View getSelectedView() { if (mItemCount > 0 && mSelectedPosition >= 0) { return getChildAt(mSelectedPosition - mFirstPosition); } else { return null; } } /** * Jump directly to a specific item in the adapter data. */ public void setSelection(int position, boolean animate) { // Animate only if requested position is already on screen somewhere boolean shouldAnimate = animate && mFirstPosition mSelectionLeftPadding ? getPaddingLeft() : mSelectionLeftPadding; mSpinnerPadding.top = getPaddingTop() > mSelectionTopPadding ? getPaddingTop() : mSelectionTopPadding; mSpinnerPadding.right = getPaddingRight() > mSelectionRightPadding ? getPaddingRight() : mSelectionRightPadding; mSpinnerPadding.bottom = getPaddingBottom() > mSelectionBottomPadding ? getPaddingBottom() : mSelectionBottomPadding; if (mDataChanged) { handleDataChanged(); } int preferred; int preferred; boolean needsMeasuring = true; int selectedPosition = getSelectedItemPosition(); if (selectedPosition >= 0 && mAdapter != null && selectedPosition CREATOR = new Parcelable.Creator () { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.selectedId = getSelectedItemId(); if (ss.selectedId >= 0) { ss.position = getSelectedItemPosition(); } else { ss.position = INVALID_POSITION; } return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); if (ss.selectedId >= 0) { mDataChanged = true; mNeedSync = true; mSyncRowId = ss.selectedId; mSyncPosition = ss.position; mSyncMode = SYNC_SELECTED_POSITION; requestLayout(); } } class RecycleBin { private final SparseArray mScrapHeap = new SparseArray (); public void put(int position, View v) { mScrapHeap.put(position, v); } View get(int position) { // System.out.print("Looking for " + position); View result = mScrapHeap.get(position); if (result != null) { // System.out.println(" HIT"); mScrapHeap.delete(position); } else { // System.out.println(" MISS"); } return result; } void clear() { final SparseArray scrapHeap = mScrapHeap; final int count = scrapHeap.size(); for (int i = 0; i
The CarouselAdapter Class
[The CarouselAdapter vs. AdapterView]
The only changes are in updateEmptyStatus method where unavailable variables were replaced with their getters.
The Carousel Class
package com.john.carousel.lib; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Camera; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Transformation; import android.widget.BaseAdapter; public class Carousel extends CarouselSpinner implements Constants { private int mAnimationDuration = 100; private int mAnimationDurationMin = 50; private Camera mCamera = null; private FlingRotateRunnable mFlingRunnable = null; private int mGravity = 0; private View mSelectedChild = null; private static int mSelectedItemIndex = 2; private boolean mShouldStopFling = false; private static final int LEFT = 0; private static final int RIGHT = 1; /** * If true, do not callback to item selected listener. */ private boolean mSuppressSelectionChanged = false; private float mTheta = 0.0f; private boolean isFocus = true; private ImageAdapter adapter = null; private static final int ONE_ITEM = 1; private CarouselItemClickListener callback = null; public Carousel(Context context) { this(context, null); } public Carousel(Context context, AttributeSet attrs) { this(context, attrs, 0); } public Carousel(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setChildrenDrawingOrderEnabled(false); setStaticTransformationsEnabled(true); TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.Carousel); int imageArrayID = arr.getResourceId(R.styleable.Carousel_Items, -1); TypedArray images = getResources().obtainTypedArray(imageArrayID); int namesForItems = arr.getResourceId(R.styleable.Carousel_Names, -1); TypedArray names = null; if (namesForItems != -1) { names = getResources().obtainTypedArray(namesForItems); } initView(images, names); arr.recycle(); images.recycle(); if (names != null) { names.recycle(); } } private void initView(TypedArray images, TypedArray names) { mCamera = new Camera(); mFlingRunnable = new FlingRotateRunnable(); mTheta = (float) (15.0f * (Math.PI / 180.0)); adapter = new ImageAdapter(getContext()); adapter.setImages(images, names); setAdapter(adapter); setSelectedPositionInt(mSelectedItemIndex); } @Override protected int computeHorizontalScrollExtent() { // Only 1 item is considered to be selected return ONE_ITEM; } @Override protected int computeHorizontalScrollOffset() { // Current scroll position is the same as the selected position return mSelectedPosition; } @Override protected int computeHorizontalScrollRange() { // Scroll range is the same as the item count return mItemCount; } public void setFocusFlag(boolean flag) { this.isFocus = flag; adapter.notifyDataSetChanged(); } public boolean getFocusFlag() { return this.isFocus; } public void setSelected(int index) { setNextSelectedPositionInt(index); mSelectedItemIndex = index; } public void setCarouselItemClickCallBack(CarouselItemClickListener listener) { callback = listener; } public interface CarouselItemClickListener { public void CarouselClickCallBack(int itemPosition); } /** * Handles left, right, and clicking * * @see android.view.View#onKeyDown */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KEY_OK: case KEY_CENTER: callback.CarouselClickCallBack(mSelectedItemIndex); return true; case KEY_LEFT: toNextLeftItem(); return true; case KEY_RIGHT: toNextRightItem(); return true; } return super.onKeyDown(keyCode, event); } @Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); /** * The gallery shows focus by focusing the selected item. So, give focus * to our selected item instead. We steal keys from our selected item * elsewhere. */ if (gainFocus && mSelectedChild != null) { mSelectedChild.requestFocus(direction); } } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LayoutParams; } @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new LayoutParams(p); } @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } @Override protected void dispatchSetPressed(boolean pressed) { if (mSelectedChild != null) { mSelectedChild.setPressed(pressed); } } @Override public boolean dispatchKeyEvent(KeyEvent event) { return false; } /** * Transform an item depending on it's coordinates */ @Override protected boolean getChildStaticTransformation(View child, Transformation transformation) { transformation.clear(); transformation.setTransformationType(Transformation.TYPE_MATRIX); // Center of the view float centerX = (float) getWidth() / 2, centerY = (float) getHeight() / 2; mCamera.save(); final Matrix matrix = transformation.getMatrix(); mCamera.translate(((CarouselItem) child).getItemX(), ((CarouselItem) child).getItemY(), ((CarouselItem) child).getItemZ()); mCamera.getMatrix(matrix); matrix.preTranslate(-centerX, -centerY); matrix.postTranslate(centerX, centerY); float[] values = new float[9]; matrix.getValues(values); mCamera.restore(); Matrix mm = new Matrix(); mm.setValues(values); ((CarouselItem) child).setCIMatrix(mm); child.invalidate(); return true; } // CarouselAdapter overrides /** * Setting up images */ void layout(int delta, boolean animate) { Log.d("ORDER", "layout"); if (mDataChanged) { handleDataChanged(); } if (mNextSelectedPosition >= 0) { setSelectedPositionInt(mNextSelectedPosition); } recycleAllViews(); detachAllViewsFromParent(); int count = getAdapter().getCount(); float angleUnit = 360.0f / count; float angleOffset = mSelectedPosition * angleUnit; for (int i = 0; i arr = new ArrayList(); for (int i = 0; i () { public int compare(CarouselItem c1, CarouselItem c2) { int a1 = (int) c1.getCurrentAngle(); if (a1 > 180) { a1 = 360 - a1; } int a2 = (int) c2.getCurrentAngle(); if (a2 > 180) { a2 = 360 - a2; } return (a1 - a2); } }); angle = arr.get(0).getCurrentAngle(); if (angle > 180.0f) { angle = -(360.0f - angle); } if (Math.abs(angle) > 0.5f) { mFlingRunnable.startUsingDistance(-angle, 1, direction); } else { position = arr.get(0).getIndex(); setSelectedPositionInt(position); onFinishedMovement(); } } public int getIndex() { return mSelectedItemIndex; } private void resetIndex() { if (mSelectedItemIndex == 7) { mSelectedItemIndex = 0; } if (mSelectedItemIndex == -1) { mSelectedItemIndex = 6; } } public void toNextRightItem() { mSelectedItemIndex = mSelectedItemIndex - 1; resetIndex(); scrollToChild(mSelectedItemIndex, RIGHT); setSelectedPositionInt(mSelectedItemIndex); } public void toNextLeftItem() { mSelectedItemIndex = mSelectedItemIndex + 1; resetIndex(); scrollToChild(mSelectedItemIndex, LEFT); setSelectedPositionInt(mSelectedItemIndex); } void scrollToChild(int i, int v) { Log.d("ORDER", "scrollToChild"); CarouselItem view = (CarouselItem) getAdapter().getView(i, null, null); Log.d("GETVIEW", "scrollToChild"); float angle = view.getCurrentAngle(); Log.d("selectCurrentAngle", "Angle:" + angle); if (angle == 0) { return; } if (angle > 180.0f) { angle = 360.0f - angle; } else { angle = -angle; } mFlingRunnable.startUsingDistance(angle, 0, v); } public void setGravity(int gravity) { if (mGravity != gravity) { mGravity = gravity; requestLayout(); } } private void setUpChild(CarouselItem child, int index, float angleOffset) { Log.d("ORDER", "setUpChild"); // Ignore any layout parameters for child, use wrap content addViewInLayout(child, -1 /* index */, generateDefaultLayoutParams()); child.setSelected(index == mSelectedPosition); int h; int w; int d; if (mInLayout) { w = child.getMeasuredWidth(); h = child.getMeasuredHeight(); d = getMeasuredWidth(); } else { w = child.getMeasuredWidth(); h = child.getMeasuredHeight(); d = getWidth(); } child.setCurrentAngle(angleOffset); child.measure(w, h); int childLeft; int childTop = calculateTop(child, true); childLeft = 0; child.layout(childLeft, childTop - 45, w, h); Calculate3DPosition(child, d, angleOffset); } /** * Tracks a motion scroll. In reality, this is used to do just about any * movement to items (touch scroll, arrow-key scroll, set an item as * selected). */ void trackMotionScroll(float deltaAngle, float deg) { Log.d("ORDER", "trackMotionScroll"); for (int i = 0; i 360.0f) { angle -= 360.0f; } while (angle
Demo测试类AndroidActivity.java
package com.john.carousel.test; import com.john.carousel.lib.Carousel; import com.john.carousel.lib.Carousel.CarouselItemClickListener; import com.john.carousel.lib.CarouselAdapter; import com.john.carousel.lib.CarouselAdapter.cOnItemClickListener; import com.john.carousel.lib.Constants; import com.john.carousel.lib.R; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnKeyListener; import android.widget.LinearLayout; public class AndroidActivity extends Activity implements CarouselItemClickListener, Constants { private Carousel carousel; private final String TAG = AndroidActivity.class.getSimpleName(); private LinearLayout layoutMain = null; private final int NETWORK = 0; private final int UPDATE = 1; private final int APK = 2; private final int STB = 3; private final int OTHER = 4; private final int WALLPAPER = 5; private final int MEDIA = 6; private int initSelection = 2; private long lastClickTime, currClickTime; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); View mainView = LayoutInflater.from(this).inflate(R.layout.activity_android, null); setContentView(mainView); if (getIntent() != null) { initSelection = getIntent().getExtras().getInt("selection", 2); } if (initSelection >= 6 || initSelection parent, View view, int position, long id) { onItemClickOrCallback(position); } }); carousel.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.equals(KeyEvent.ACTION_DOWN)) { switch (keyCode) { case KEY_LEFT: carousel.toNextLeftItem(); break; case KEY_RIGHT: carousel.toNextRightItem(); break; case KEY_OK: case KEY_CENTER: onItemClickOrCallback(carousel.getIndex()); break; } } carouselGetFocus(); return true; } }); } private void onItemClickOrCallback(int position) { switch (position) { case NETWORK: break; case UPDATE: break; case APK: break; case STB: break; case OTHER: break; case WALLPAPER: break; case MEDIA: break; default: break; } } @Override public void CarouselClickCallBack(int itemPosition) { onItemClickOrCallback(itemPosition); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KEY_OK: case KEY_CENTER: onItemClickOrCallback(carousel.getIndex()); return true; case KEY_LEFT: if (carousel.getFocusFlag()) { currClickTime = System.currentTimeMillis(); if (currClickTime - lastClickTime > 200) { lastClickTime = currClickTime; carousel.toNextLeftItem(); Log.d("selectedItemIndex", carousel.getIndex() + ""); return true; } else { return true; } } break; case KEY_RIGHT: if (carousel.getFocusFlag()) { currClickTime = System.currentTimeMillis(); if (currClickTime - lastClickTime > 200) { lastClickTime = currClickTime; carousel.toNextRightItem(); Log.d("selectedItemIndex", carousel.getIndex() + ""); return true; } else { return true; } } break; case KEY_UP: carousel.setFocusFlag(false); carousel.clearFocus(); carousel.setFocusable(false); carousel.setSelected(false); return true; case KEY_DOWN: if (!carousel.getFocusFlag()) { Log.e(TAG, "KEY_DOWN"); carouselGetFocus(); } return true; case KEY_EXIT: return true; case KEY_VOLDOWN: case KEY_VOLUP: case KEY_MUTE: case KEY_VOLUME_MUTE: return true; } return super.onKeyDown(keyCode, event); } private void carouselGetFocus() { carousel.setFocusFlag(true); carousel.requestFocus(); carousel.setFocusable(true); } }
相关文章
- 三国群英传策定九州如何发展建筑 09-16
- 三国望神州郭嘉怎么玩 09-16
- 地下城堡4骑士与破碎编年史奥术顾问阿黛拉有什么技能 09-16
- 怪兽8号手游核爆流队伍怎么搭配 09-16
- 下一站江湖2流光追影有哪些途径获得 09-16
- 失控进化雪山如何探索 09-16