是否可以继承android.content.ClipData或ClipData.Item?

时间:2014-03-29 10:21:51

标签: java android inheritance

基本上,我想要ClipData.Item的子类,以便我可以发送除CharSequenceIntentURI以外的数据以及DragEvent。文档似乎表明这是可能的(请参阅documentation旁边的toString()方法,该方法专门提到了Item的子类,但是我尝试过的所有方法都没有用,尽管它们都没有{ {1}}也未ClipData被宣布为Item

我得到的基本设置是扩展final的内部类,如下所示:

ClipData.Item

TowerButton.java

然后,在接受package com.conundrum.toweroffensenative.app; import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import java.util.List; /** * Created by Nums on 27/03/14. */ public class TowerButton extends View { private Paint mBackgroundPaint, mTowerPaint, mShadowPaint, mLabelPaint, mDisabledPaint; private List<Tower> mTowers; private Path mTowerPath; private DragShadowBuilder mDragShadowBuilder; private Rect r; public TowerButton(Context context, AttributeSet attrs, List<Tower> towers) { super(context, attrs); mTowers = towers; init(); } // If I need a tower type that starts with 0 stock, add constructor which takes Paint/Path as args private void init() { mBackgroundPaint = new Paint(); mBackgroundPaint.setStyle(Paint.Style.FILL); mBackgroundPaint.setColor(Color.GRAY); mTowerPaint = mTowers.get(0).getPaint(); mTowerPath = mTowers.get(0).getPath(); mShadowPaint = new Paint(mTowerPaint); mShadowPaint.setAlpha(150); mDisabledPaint = new Paint(mTowerPaint); mDisabledPaint.setColor(Color.LTGRAY); mDisabledPaint.setAlpha(150); mLabelPaint = new Paint(); mLabelPaint.setTextSize(28); mLabelPaint.setTextAlign(Paint.Align.CENTER); mLabelPaint.setAntiAlias(true); mLabelPaint.setColor(Color.WHITE); mDragShadowBuilder = new DragShadowBuilder(this) { @Override public void onDrawShadow(Canvas canvas) { canvas.drawPath(mTowerPath, mShadowPaint); } }; setTag(mTowers.get(0).getClass().getName() + "Button"); r = new Rect(); } public String getQuantity() { return String.valueOf(mTowers.size()); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); Matrix pathMatrix = new Matrix(); RectF pathBounds = new RectF(); mTowerPath.computeBounds(pathBounds, false); pathMatrix.setScale(w / pathBounds.width(), h / pathBounds.height()); mTowerPath.transform(pathMatrix); r.set(0, 0, w, h); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawRect(r, mBackgroundPaint); if (mTowers.size() > 0) { canvas.drawPath(mTowerPath, mTowerPaint); canvas.drawText(getQuantity(), getX() + (getWidth() / 2), getY() + (getHeight() / 2), mLabelPaint); } else { canvas.drawPath(mTowerPath, mDisabledPaint); } } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN && mTowers.size() > 0) { Tower dragTower = mTowers.get(0); TowerItem item = new TowerItem(dragTower); ClipData dragData = new ClipData(dragTower.getBuildRow(), new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN }, item); startDrag(dragData, mDragShadowBuilder, null, 0); return true; } return false; } public Tower giveTower() { // TODO: need checking to ensure size > 0? Tower tower = mTowers.remove(0); invalidate(); return tower; } public void recycleTower(Tower tower) { mTowers.add(tower); invalidate(); } public static class TowerItem extends ClipData.Item { final Tower mTower; public TowerItem(Tower tower) { super(""); mTower = tower; } public Tower getTower() { return mTower; } @Override public CharSequence coerceToText(Context context) { if (mTower != null) { return mTower.getClass().getName(); } return super.coerceToText(context); } } }

的班级中

DropEvent

TowerView.java

没有编译时错误。但是,在尝试在运行时执行转换时,我得到了异常:

package com.conundrum.toweroffensenative.app; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.ClipData; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.DragEvent; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; /** * Created by Nums on 24/03/14. */ public class TowerView extends View { private Paint mBasePaint, mHighlightPaint, mStunnedPaint, mSelectedPaint; private Tower mTower; private Path mTowerPath; private Paint mTowerPaint; private boolean highlighted; private boolean stunned; private boolean selected; private int mIndex; private List<TowerView> mNeighbours; private Rect r; private class mListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onSingleTapUp(MotionEvent e) { for (TowerView tv : mNeighbours) { tv.highlighted ^= true; tv.invalidate(); } return true; } @Override public void onLongPress(MotionEvent e) { List<TowerView> myRow = ((TowerGrid) getParent()).getRow(mIndex % TowerGrid.ROWS); for (TowerView v : myRow) { v.stunned ^= true; v.invalidate(); } } } GestureDetector mDetector = new GestureDetector(TowerView.this.getContext(), new mListener()); Callable<Void> mStartRecycleCallable = new Callable<Void>() { @Override public Void call() throws Exception { startRecycle(); return null; } }; Callable<Void> mRecycleCallable = new Callable<Void>() { @Override public Void call() throws Exception { recycle(); return null; } }; public TowerView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { mIndex = -1; mNeighbours = new ArrayList<TowerView>(); highlighted = false; stunned = false; selected = false; LinearGradient baseGradient = new LinearGradient(0, 0, 0, 25, new int[] {Color.LTGRAY, Color.DKGRAY}, null, Shader.TileMode.MIRROR); LinearGradient highlightGradient = new LinearGradient(0, 0, 0, 25, new int[] {Color.YELLOW, Color.RED}, null, Shader.TileMode.MIRROR); LinearGradient stunnedGradient = new LinearGradient(0, 0, 0, 25, new int[] {Color.CYAN, Color.BLUE}, null, Shader.TileMode.MIRROR); mBasePaint = new Paint(); mBasePaint.setShader(baseGradient); mHighlightPaint = new Paint(); mHighlightPaint.setShader(highlightGradient); mStunnedPaint = new Paint(); mStunnedPaint.setShader(stunnedGradient); mSelectedPaint = new Paint(); mSelectedPaint.setStyle(Paint.Style.STROKE); mSelectedPaint.setColor(Color.GREEN); mSelectedPaint.setStrokeWidth(5); r = new Rect(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); r.set(0, 0, w, h); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // Draw the tower base in one of three styles if (highlighted) { canvas.drawRect(r, mHighlightPaint); } else if (stunned) { canvas.drawRect(r, mStunnedPaint); } else { canvas.drawRect(r, mBasePaint); } if (mTower != null) { canvas.drawPath(mTowerPath, mTowerPaint); } if (selected) { canvas.drawRect(r, mSelectedPaint); } } @Override public boolean onTouchEvent(MotionEvent event) { boolean result = mDetector.onTouchEvent(event); if (!result) { // Custom gesture code } return result; } @Override public boolean dispatchTouchEvent(MotionEvent event) { boolean result = super.dispatchTouchEvent(event); return result; } @Override public boolean onDragEvent(DragEvent event) { final int action = event.getAction(); switch(action) { case DragEvent.ACTION_DRAG_STARTED: // check if Tower can be built on this col - in case I allow that to differ per Tower if (mIndex / TowerGrid.ROWS == Integer.parseInt(event.getClipDescription().getLabel().toString())) { selected = true; invalidate(); return true; } return false; case DragEvent.ACTION_DRAG_ENTERED: highlighted = true; invalidate(); break; case DragEvent.ACTION_DRAG_EXITED: highlighted = false; invalidate(); break; case DragEvent.ACTION_DROP: ClipData.Item item = event.getClipData().getItemAt(0); if (item instanceof TowerButton.TowerItem) { Log.d("towerview", "SUCCESS!"); } // Always returns false TowerButton.TowerItem tItem = (TowerButton.TowerItem) item; // exception Tower dragTower = item.getTower(); setTower(dragTower); return true; case DragEvent.ACTION_DRAG_ENDED: highlighted = false; selected = false; invalidate(); return true; } return false; } public void setTower(Tower tower) { if (mTower != null) { TowerButton button = (TowerButton) getRootView().findViewWithTag(mTower.getClass().getName() + "Button"); button.recycleTower(mTower); } mTower = tower; mTowerPaint = tower.getPaint(); mTowerPath = tower.getPath(); Matrix pathMatrix = new Matrix(); RectF pathBounds = new RectF(); mTowerPath.computeBounds(pathBounds, false); pathMatrix.setScale(getWidth() / pathBounds.width(), getHeight() / pathBounds.height()); mTowerPath.transform(pathMatrix); invalidate(); } public boolean advance(int distance) { if (!stunned) { // first account for the new view being added setTranslationX(getTranslationX() - distance); // then animate right over 1000 ms ViewPropertyAnimator animator = animate().translationXBy(distance).setDuration(1000); addCompatibilityAnimationCallback(animator, mStartRecycleCallable).start(); return true; } return false; } private void startRecycle() { if (mIndex / TowerGrid.ROWS == TowerGrid.COLS - 1) { ViewPropertyAnimator animator = animate().translationXBy(getWidth() / -2).scaleX(0).setDuration(1000); addCompatibilityAnimationCallback(animator, mRecycleCallable).start(); } } private void recycle() { if (mTower != null) { TowerButton button = (TowerButton) getRootView().findViewWithTag(mTower.getClass().getName() + "Button"); button.recycleTower(mTower); } ((ViewGroup) getParent()).removeView(this); } public void updateNeighbours() { ViewGroup parent = (ViewGroup) getParent(); mIndex = parent.indexOfChild(this); mNeighbours.clear(); if (mIndex >= TowerGrid.ROWS) { mNeighbours.add((TowerView) parent.getChildAt(mIndex - TowerGrid.ROWS)); } if (mIndex < TowerGrid.ROWS * (TowerGrid.COLS - 2)) { mNeighbours.add((TowerView) parent.getChildAt(mIndex + TowerGrid.ROWS)); } if (mIndex % TowerGrid.ROWS != 0) { mNeighbours.add((TowerView) parent.getChildAt(mIndex - 1)); } if (mIndex % TowerGrid.ROWS != TowerGrid.ROWS - 1) { mNeighbours.add((TowerView) parent.getChildAt(mIndex + 1)); } } private ViewPropertyAnimator addCompatibilityAnimationCallback(ViewPropertyAnimator animator, final Callable<Void> callbackFunc) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { animator.setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { try { callbackFunc.call(); } catch (Exception e) { e.printStackTrace(); } } }); } else { animator.withEndAction(new Runnable() { @Override public void run() { try { callbackFunc.call(); } catch (Exception e) { e.printStackTrace(); } } }); } return animator; } }

同样,java.lang.ClassCastException: android.content.ClipData$Item cannot be cast to com.conundrum.toweroffensenative.app.TowerButton$TowerItem之类的代码会返回false,即使item instanceof TowerButton.TowerItem显然正在扩展TowerItem

我是否缺少某些东西阻止这些类被子类化?或者我做错了什么?我知道我可以使用ClipData.ItemContentProvider来传输更复杂的信息,但是当传输的数据永远不必在此应用程序之外可用时,这似乎有点过分了。

修改

我还尝试使用自己的URI内部类创建ClipData的子类,以便我可以覆盖Item以返回getItem() - 然后我需要将TowerItem转换为ClipData,但失败时会出现同样的错误。

编辑3

包括两个相关文件的全部内容。

2 个答案:

答案 0 :(得分:0)

TowerItem来自ClipData.Item,因此TowerItemClipData.Item,但反过来并非总是如此。 ClipData.Item可以是TowerItem,但不一定。

通过将ClipData.Item显式转换为TowerItem(TowerItem)ClipData.Item来避免编译错误。但是你无法避免运行时错误。

instanceOf的正确用法应该是这样的:

if(event.getClipData().getItemAt(0) instanceOf TowerButton.TowerItem) {
    TowerButton.TowerItem item = (TowerButton.TowerItem) event.getClipData().getItemAt(0);
}

答案 1 :(得分:0)

类似的问题,我一直在为此考虑系统代码。基本上,ClipData是一个Parcelable,所以我认为ClipData.Item的一个子类没有一个敏感的ClipData子类,它识别你的自定义ClipData.Item将转换为一个Charsequence然后作为基本文本返回ClipData.Item。否则,您需要为URI格式创建一个ContentProvider,对于这个用例,单个应用程序中的ui是过度的,可能是错误的。 我对此的破解最终是将ClipData中的标识标记作为文本传递,并使拖动启动视图(通过状态传递到事件数据中)负责将其转换为对象。不完全干净但不完全丑陋。