diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index eddada39b..b425eaa11 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -1864,6 +1864,15 @@ + + + + + + + diff --git a/samples/ApiDemos/res/layout/drag_layout.xml b/samples/ApiDemos/res/layout/drag_layout.xml new file mode 100644 index 000000000..0dd193d03 --- /dev/null +++ b/samples/ApiDemos/res/layout/drag_layout.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/ApiDemos/res/values/attrs.xml b/samples/ApiDemos/res/values/attrs.xml index 53f0034e8..4654d7e87 100644 --- a/samples/ApiDemos/res/values/attrs.xml +++ b/samples/ApiDemos/res/values/attrs.xml @@ -32,4 +32,17 @@ + + + + + + + + + + + + diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml index e988a2f3b..4eb54c2a9 100644 --- a/samples/ApiDemos/res/values/strings.xml +++ b/samples/ApiDemos/res/values/strings.xml @@ -758,6 +758,12 @@ Cheese hunt Expand Iconify + + + Longpress on a dot to start a drag, then drop over another dot. The destination + dot will append the drag\'s textual conversion to the EditText. + + diff --git a/samples/ApiDemos/src/com/example/android/apis/view/DragAndDropDemo.java b/samples/ApiDemos/src/com/example/android/apis/view/DragAndDropDemo.java new file mode 100644 index 000000000..7e8c076e4 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/view/DragAndDropDemo.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 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.apis.view; + +import com.example.android.apis.R; + +import android.app.Activity; +import android.os.Bundle; +import android.view.DragEvent; +import android.view.View; +import android.widget.TextView; + +public class DragAndDropDemo extends Activity { + TextView mResultText; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.drag_layout); + + TextView text = (TextView) findViewById(R.id.drag_text); + DraggableDot dot = (DraggableDot) findViewById(R.id.drag_dot_1); + dot.setReportView(text); + dot = (DraggableDot) findViewById(R.id.drag_dot_2); + dot.setReportView(text); + dot = (DraggableDot) findViewById(R.id.drag_dot_3); + dot.setReportView(text); + dot = (DraggableDot) findViewById(R.id.drag_dot_4); + dot.setReportView(text); + + mResultText = (TextView) findViewById(R.id.drag_result_text); + mResultText.setOnDragListener(new View.OnDragListener() { + @Override + public boolean onDrag(View v, DragEvent event) { + final int action = event.getAction(); + if (action == DragEvent.ACTION_DRAG_ENDED) { + final boolean dropped = event.getResult(); + mResultText.setText(dropped ? "Dropped!" : "No drop"); + } + return false; + } + }); + } +} \ No newline at end of file diff --git a/samples/ApiDemos/src/com/example/android/apis/view/DraggableDot.java b/samples/ApiDemos/src/com/example/android/apis/view/DraggableDot.java new file mode 100644 index 000000000..b715ba1a9 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/view/DraggableDot.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2010 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.apis.view; + +import com.example.android.apis.R; + +import android.content.ClipData; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.*; +import android.os.SystemClock; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.util.Log; +import android.view.DragEvent; +import android.view.View; +import android.widget.TextView; + +public class DraggableDot extends View { + static final String TAG = "DraggableDot"; + + private boolean mDragInProgress; + private boolean mHovering; + private boolean mAcceptsDrag; + TextView mReportView; + + private Paint mPaint; + private TextPaint mLegendPaint; + private Paint mGlow; + private static final int NUM_GLOW_STEPS = 10; + private static final int GREEN_STEP = 0x0000FF00 / NUM_GLOW_STEPS; + private static final int WHITE_STEP = 0x00FFFFFF / NUM_GLOW_STEPS; + private static final int ALPHA_STEP = 0xFF000000 / NUM_GLOW_STEPS; + + int mRadius; + int mAnrType; + boolean mLocalOnly; + CharSequence mLegend; + + static final int ANR_NONE = 0; + static final int ANR_THUMBNAIL = 1; + static final int ANR_DROP = 2; + + void sleepSixSeconds() { + // hang forever; good for producing ANRs + long start = SystemClock.uptimeMillis(); + do { + try { Thread.sleep(1000); } catch (InterruptedException e) {} + } while (SystemClock.uptimeMillis() < start + 6000); + } + + // Thumbnail builder that can ANR if desired + class ANRThumbBuilder extends DragThumbnailBuilder { + boolean mDoAnr; + + public ANRThumbBuilder(View view, boolean doAnr) { + super(view); + mDoAnr = doAnr; + } + + @Override + public void onDrawThumbnail(Canvas canvas) { + if (mDoAnr) { + sleepSixSeconds(); + } + super.onDrawThumbnail(canvas); + } + } + + public DraggableDot(Context context, AttributeSet attrs) { + super(context, attrs); + + setFocusable(true); + setClickable(true); + + mLegend = ""; + + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setStrokeWidth(6); + mPaint.setColor(0xFFD00000); + + mLegendPaint = new TextPaint(); + mLegendPaint.setAntiAlias(true); + mLegendPaint.setTextAlign(Paint.Align.CENTER); + mLegendPaint.setColor(0xFFF0F0FF); + + mGlow = new Paint(); + mGlow.setAntiAlias(true); + mGlow.setStrokeWidth(1); + mGlow.setStyle(Paint.Style.STROKE); + + // look up any layout-defined attributes + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.DraggableDot); + + final int N = a.getIndexCount(); + for (int i = 0; i < N; i++) { + int attr = a.getIndex(i); + switch (attr) { + case R.styleable.DraggableDot_radius: { + mRadius = a.getDimensionPixelSize(attr, 0); + } break; + + case R.styleable.DraggableDot_legend: { + mLegend = a.getText(attr); + } break; + + case R.styleable.DraggableDot_anr: { + mAnrType = a.getInt(attr, 0); + } break; + + case R.styleable.DraggableDot_localOnly: { + mLocalOnly = a.getBoolean(attr, false); + } break; + } + } + + Log.i(TAG, "DraggableDot @ " + this + " : radius=" + mRadius + " legend='" + mLegend + + "' anr=" + mAnrType + " local=" + mLocalOnly); + + setOnLongClickListener(new View.OnLongClickListener() { + public boolean onLongClick(View v) { + ClipData data = ClipData.newPlainText("dot", null, "Dot : " + v.toString()); + v.startDrag(data, new ANRThumbBuilder(v, mAnrType == ANR_THUMBNAIL), + mLocalOnly, (Object)v); + return true; + } + }); + } + + void setReportView(TextView view) { + mReportView = view; + } + + @Override + protected void onDraw(Canvas canvas) { + float wf = getWidth(); + float hf = getHeight(); + final float cx = wf/2; + final float cy = hf/2; + wf -= getPaddingLeft() + getPaddingRight(); + hf -= getPaddingTop() + getPaddingBottom(); + float rad = (wf < hf) ? wf/2 : hf/2; + canvas.drawCircle(cx, cy, rad, mPaint); + + if (mLegend != null && mLegend.length() > 0) { + canvas.drawText(mLegend, 0, mLegend.length(), + cx, cy + mLegendPaint.getFontSpacing()/2, + mLegendPaint); + } + + // if we're in the middle of a drag, light up as a potential target + if (mDragInProgress && mAcceptsDrag) { + for (int i = NUM_GLOW_STEPS; i > 0; i--) { + int color = (mHovering) ? WHITE_STEP : GREEN_STEP; + color = i*(color | ALPHA_STEP); + mGlow.setColor(color); + canvas.drawCircle(cx, cy, rad, mGlow); + rad -= 0.5f; + canvas.drawCircle(cx, cy, rad, mGlow); + rad -= 0.5f; + } + } + } + + @Override + protected void onMeasure(int widthSpec, int heightSpec) { + int totalDiameter = 2*mRadius + getPaddingLeft() + getPaddingRight(); + setMeasuredDimension(totalDiameter, totalDiameter); + } + + /** + * Drag and drop + */ + @Override + public boolean onDragEvent(DragEvent event) { + boolean result = false; + switch (event.getAction()) { + case DragEvent.ACTION_DRAG_STARTED: { + // claim to accept any dragged content + Log.i(TAG, "Drag started, event=" + event); + // cache whether we accept the drag to return for LOCATION events + mDragInProgress = true; + mAcceptsDrag = result = true; + // Redraw in the new visual state if we are a potential drop target + if (mAcceptsDrag) { + invalidate(); + } + } break; + + case DragEvent.ACTION_DRAG_ENDED: { + Log.i(TAG, "Drag ended."); + if (mAcceptsDrag) { + invalidate(); + } + mDragInProgress = false; + mHovering = false; + } break; + + case DragEvent.ACTION_DRAG_LOCATION: { + // we returned true to DRAG_STARTED, so return true here + Log.i(TAG, "... seeing drag locations ..."); + result = mAcceptsDrag; + } break; + + case DragEvent.ACTION_DROP: { + Log.i(TAG, "Got a drop! dot=" + this + " event=" + event); + if (mAnrType == ANR_DROP) { + sleepSixSeconds(); + } + processDrop(event); + result = true; + } break; + + case DragEvent.ACTION_DRAG_ENTERED: { + Log.i(TAG, "Entered dot @ " + this); + mHovering = true; + invalidate(); + } break; + + case DragEvent.ACTION_DRAG_EXITED: { + Log.i(TAG, "Exited dot @ " + this); + mHovering = false; + invalidate(); + } break; + + default: + Log.i(TAG, "other drag event: " + event); + result = mAcceptsDrag; + break; + } + + return result; + } + + private void processDrop(DragEvent event) { + final ClipData data = event.getClipData(); + final int N = data.getItemCount(); + for (int i = 0; i < N; i++) { + ClipData.Item item = data.getItem(i); + Log.i(TAG, "Dropped item " + i + " : " + item); + if (mReportView != null) { + String text = item.coerceToText(getContext()).toString(); + if (event.getLocalState() == (Object) this) { + text += " : Dropped on self!"; + } + mReportView.setText(text); + } + } + } +} \ No newline at end of file