java是安卓的子类。所容纳之物ClipData或ClipData。项目
基本上,我想将ClipData.Item
子类化,这样我就可以发送除CharSequence
、Intent
或URI
之外的数据以及DragEvent
。文档似乎暗示这是可能的(参见toString()
方法旁边的documentation,它特别提到了Item
的子类),但是尽管ClipData
和Item
都没有被声明final
,我尝试的一切都没有起作用
我得到的基本设置是一个扩展ClipData.Item
的内部类,如下所示:
TowerButton.java
package com.conundrum.toweroffensenative.app;
import 安卓.content.ClipData;
import 安卓.content.ClipDescription;
import 安卓.content.Context;
import 安卓.graphics.Canvas;
import 安卓.graphics.Color;
import 安卓.graphics.Matrix;
import 安卓.graphics.Paint;
import 安卓.graphics.Path;
import 安卓.graphics.Rect;
import 安卓.graphics.RectF;
import 安卓.util.AttributeSet;
import 安卓.view.MotionEvent;
import 安卓.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 安卓.animation.Animator;
import 安卓.animation.AnimatorListenerAdapter;
import 安卓.content.ClipData;
import 安卓.content.Context;
import 安卓.graphics.Canvas;
import 安卓.graphics.Color;
import 安卓.graphics.LinearGradient;
import 安卓.graphics.Matrix;
import 安卓.graphics.Paint;
import 安卓.graphics.Path;
import 安卓.graphics.Rect;
import 安卓.graphics.RectF;
import 安卓.graphics.Shader;
import 安卓.os.Build;
import 安卓.util.AttributeSet;
import 安卓.util.Log;
import 安卓.view.DragEvent;
import 安卓.view.GestureDetector;
import 安卓.view.MotionEvent;
import 安卓.view.View;
import 安卓.view.ViewGroup;
import 安卓.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: 安卓.content.ClipData$Item cannot be cast to com.conundrum.toweroffensenative.app.TowerButton$TowerItem
类似地,类似于item instanceof TowerButton.TowerItem
的代码返回false,即使TowerItem
显然是在扩展ClipData.Item
我是否缺少一些东西来阻止这些类被子类化?还是我做错了什么?我知道我可以使用ContentProvider
和URI
来传输更复杂的信息,但当传输的数据不必在该应用程序之外可用时,这似乎有些过分
编辑
我还尝试用它自己的Item
内部类来创建ClipData
的子类,这样我就可以重写getItem()
来返回TowerItem
,但是接下来我需要将ClipData
转换为TowerClipData
,这会导致同样的错误
编辑3
包括两个相关文件的全部内容
# 1 楼答案
TowerItem
是ClipData.Item
的后代,因此TowerItem
是ClipData.Item
,但反过来并不总是正确的。AClipData.Item
可以是ATowerItem
,但不一定是通过显式地将
ClipData.Item
强制转换为TowerItem
:(TowerItem)ClipData.Item
,可以避免编译器错误。但您无法避免运行时错误正确使用
instanceOf
应该是这样的:# 2 楼答案
类似的问题,我已经检查了系统代码。基本上,ClipData是一个可包裹的,所以我认为它是ClipData的一个子类。没有可识别自定义ClipData的合理ClipData子类的项。项将被转换为字符序列,然后作为基本文本剪辑数据返回。项目否则,您需要为URI格式创建一个ContentProvider,在本用例中,它是单个应用程序中的一个ui,这是过分的,可能是错误的。 我的技巧是最终将ClipData中的一个识别标记作为文本传递,并使拖动启动视图(通过状态传递事件数据)负责将其转化为一个对象。不完全干净,但也不完全丑陋