Wire up SurfaceView stretch effect
Bug: 179047472 Test: StretchySurfaceViewActivity Change-Id: I7f3d582cc66fb732a557e9332edc6d186db2335c
This commit is contained in:
@@ -141,6 +141,9 @@ public final class SurfaceControl implements Parcelable {
|
||||
int layerStack);
|
||||
private static native void nativeSetBlurRegions(long transactionObj, long nativeObj,
|
||||
float[][] regions, int length);
|
||||
private static native void nativeSetStretchEffect(long transactionObj, long nativeObj,
|
||||
float left, float top, float right, float bottom, float vecX, float vecY,
|
||||
float maxStretchAmount);
|
||||
|
||||
private static native boolean nativeClearContentFrameStats(long nativeObject);
|
||||
private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats);
|
||||
@@ -2948,6 +2951,17 @@ public final class SurfaceControl implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public Transaction setStretchEffect(SurfaceControl sc, float left, float top, float right,
|
||||
float bottom, float vecX, float vecY, float maxStretchAmount) {
|
||||
checkPreconditions(sc);
|
||||
nativeSetStretchEffect(mNativeObject, sc.mNativeObject, left, top, right, bottom,
|
||||
vecX, vecY, maxStretchAmount);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@@ -1443,6 +1443,14 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStretch(long frameNumber, float left, float top, float right,
|
||||
float bottom, float vecX, float vecY, float maxStretch) {
|
||||
mRtTransaction.setStretchEffect(mSurfaceControl, left, top, right, bottom, vecX, vecY,
|
||||
maxStretch);
|
||||
applyRtTransaction(frameNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void positionLost(long frameNumber) {
|
||||
if (DEBUG) {
|
||||
|
||||
@@ -580,6 +580,15 @@ static void nativeSetBlurRegions(JNIEnv* env, jclass clazz, jlong transactionObj
|
||||
transaction->setBlurRegions(ctrl, blurRegionVector);
|
||||
}
|
||||
|
||||
static void nativeSetStretchEffect(JNIEnv* env, jclass clazz, jlong transactionObj,
|
||||
jlong nativeObject, jfloat left, jfloat top, jfloat right,
|
||||
jfloat bottom, jfloat vecX, jfloat vecY,
|
||||
jfloat maxStretchAmount) {
|
||||
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
|
||||
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
|
||||
transaction->setStretchEffect(ctrl, left, top, right, bottom, vecX, vecY, maxStretchAmount);
|
||||
}
|
||||
|
||||
static void nativeSetSize(JNIEnv* env, jclass clazz, jlong transactionObj,
|
||||
jlong nativeObject, jint w, jint h) {
|
||||
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
|
||||
@@ -1754,6 +1763,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
|
||||
(void*)nativeSetLayerStack },
|
||||
{"nativeSetBlurRegions", "(JJ[[FI)V",
|
||||
(void*)nativeSetBlurRegions },
|
||||
{"nativeSetStretchEffect", "(JJFFFFFFF)V",
|
||||
(void*) nativeSetStretchEffect },
|
||||
{"nativeSetShadowRadius", "(JJF)V",
|
||||
(void*)nativeSetShadowRadius },
|
||||
{"nativeSetFrameRate", "(JJFIZ)V",
|
||||
|
||||
@@ -271,6 +271,16 @@ public final class RenderNode {
|
||||
*/
|
||||
void positionChanged(long frameNumber, int left, int top, int right, int bottom);
|
||||
|
||||
/**
|
||||
* Call to apply a stretch effect to any child SurfaceControl layers
|
||||
*
|
||||
* TODO: Fold this into positionChanged & have HWUI do the ASurfaceControl calls?
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
default void applyStretch(long frameNumber, float left, float top, float right,
|
||||
float bottom, float vecX, float vecY, float maxStretch) { }
|
||||
|
||||
/**
|
||||
* Called by native on RenderThread to notify that the view is no longer in the
|
||||
* draw tree. UI thread is blocked at this point.
|
||||
@@ -312,6 +322,14 @@ public final class RenderNode {
|
||||
pul.positionLost(frameNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStretch(long frameNumber, float left, float top, float right, float bottom,
|
||||
float vecX, float vecY, float maxStretch) {
|
||||
for (PositionUpdateListener pul : mListeners) {
|
||||
pul.applyStretch(frameNumber, left, top, right, bottom, vecX, vecY, maxStretch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -707,7 +725,7 @@ public final class RenderNode {
|
||||
if (1.0 < vecY || vecY < -1.0) {
|
||||
throw new IllegalArgumentException("vecY must be in the range [-1, 1], was " + vecY);
|
||||
}
|
||||
if (top <= bottom || right <= left) {
|
||||
if (top >= bottom || left >= right) {
|
||||
throw new IllegalArgumentException(
|
||||
"Stretch region must not be empty, got "
|
||||
+ new RectF(left, top, right, bottom).toString());
|
||||
|
||||
@@ -249,5 +249,20 @@ void DamageAccumulator::finish(SkRect* totalDirty) {
|
||||
mHead->pendingDirty.setEmpty();
|
||||
}
|
||||
|
||||
const StretchEffect* DamageAccumulator::findNearestStretchEffect() const {
|
||||
DirtyStack* frame = mHead;
|
||||
while (frame->prev != frame) {
|
||||
frame = frame->prev;
|
||||
if (frame->type == TransformRenderNode) {
|
||||
const auto& effect =
|
||||
frame->renderNode->properties().layerProperties().getStretchEffect();
|
||||
if (!effect.isEmpty()) {
|
||||
return &effect;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace uirenderer {
|
||||
struct DirtyStack;
|
||||
class RenderNode;
|
||||
class Matrix4;
|
||||
class StretchEffect;
|
||||
|
||||
class DamageAccumulator {
|
||||
PREVENT_COPY_AND_ASSIGN(DamageAccumulator);
|
||||
@@ -62,6 +63,8 @@ public:
|
||||
|
||||
void finish(SkRect* totalDirty);
|
||||
|
||||
const StretchEffect* findNearestStretchEffect() const;
|
||||
|
||||
private:
|
||||
void pushCommon();
|
||||
void applyMatrix4Transform(DirtyStack* frame);
|
||||
|
||||
@@ -226,6 +226,9 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu
|
||||
if (!mProperties.getAllowForceDark()) {
|
||||
info.disableForceDark++;
|
||||
}
|
||||
if (!mProperties.layerProperties().getStretchEffect().isEmpty()) {
|
||||
info.stretchEffectCount++;
|
||||
}
|
||||
|
||||
uint32_t animatorDirtyMask = 0;
|
||||
if (CC_LIKELY(info.runAnimations)) {
|
||||
@@ -267,6 +270,9 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu
|
||||
if (!mProperties.getAllowForceDark()) {
|
||||
info.disableForceDark--;
|
||||
}
|
||||
if (!mProperties.layerProperties().getStretchEffect().isEmpty()) {
|
||||
info.stretchEffectCount--;
|
||||
}
|
||||
info.damageAccumulator->popTransform();
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ LayerProperties& LayerProperties::operator=(const LayerProperties& other) {
|
||||
setXferMode(other.xferMode());
|
||||
setColorFilter(other.getColorFilter());
|
||||
setImageFilter(other.getImageFilter());
|
||||
mStretchEffect = other.mStretchEffect;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,8 @@ public:
|
||||
|
||||
const SkISize screenSize;
|
||||
|
||||
int stretchEffectCount = 0;
|
||||
|
||||
struct Out {
|
||||
bool hasFunctors = false;
|
||||
// This is only updated if evaluateAnimations is true
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <renderthread/CanvasContext.h>
|
||||
#endif
|
||||
#include <TreeInfo.h>
|
||||
#include <effects/StretchEffect.h>
|
||||
#include <hwui/Paint.h>
|
||||
#include <utils/TraceUtils.h>
|
||||
|
||||
@@ -549,6 +550,7 @@ static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz,
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
jmethodID gPositionListener_PositionChangedMethod;
|
||||
jmethodID gPositionListener_ApplyStretchMethod;
|
||||
jmethodID gPositionListener_PositionLostMethod;
|
||||
|
||||
static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
|
||||
@@ -571,6 +573,11 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
|
||||
Matrix4 transform;
|
||||
info.damageAccumulator->computeCurrentTransform(&transform);
|
||||
const RenderProperties& props = node.properties();
|
||||
|
||||
if (info.stretchEffectCount) {
|
||||
handleStretchEffect(info, transform);
|
||||
}
|
||||
|
||||
uirenderer::Rect bounds(props.getWidth(), props.getHeight());
|
||||
transform.mapRect(bounds);
|
||||
|
||||
@@ -613,7 +620,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
|
||||
JNIEnv* env = jnienv();
|
||||
jobject localref = env->NewLocalRef(mWeakRef);
|
||||
if (CC_UNLIKELY(!localref)) {
|
||||
jnienv()->DeleteWeakGlobalRef(mWeakRef);
|
||||
env->DeleteWeakGlobalRef(mWeakRef);
|
||||
mWeakRef = nullptr;
|
||||
return;
|
||||
}
|
||||
@@ -634,6 +641,32 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
|
||||
return env;
|
||||
}
|
||||
|
||||
void handleStretchEffect(const TreeInfo& info, const Matrix4& transform) {
|
||||
// Search up to find the nearest stretcheffect parent
|
||||
const StretchEffect* effect = info.damageAccumulator->findNearestStretchEffect();
|
||||
if (!effect) {
|
||||
return;
|
||||
}
|
||||
|
||||
uirenderer::Rect area = effect->stretchArea;
|
||||
transform.mapRect(area);
|
||||
JNIEnv* env = jnienv();
|
||||
|
||||
jobject localref = env->NewLocalRef(mWeakRef);
|
||||
if (CC_UNLIKELY(!localref)) {
|
||||
env->DeleteWeakGlobalRef(mWeakRef);
|
||||
mWeakRef = nullptr;
|
||||
return;
|
||||
}
|
||||
#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
|
||||
env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod,
|
||||
info.canvasContext.getFrameNumber(), area.left, area.top,
|
||||
area.right, area.bottom, effect->stretchDirection.fX,
|
||||
effect->stretchDirection.fY, effect->maxStretchAmount);
|
||||
#endif
|
||||
env->DeleteLocalRef(localref);
|
||||
}
|
||||
|
||||
void doUpdatePositionAsync(jlong frameNumber, jint left, jint top,
|
||||
jint right, jint bottom) {
|
||||
ATRACE_NAME("Update SurfaceView position");
|
||||
@@ -775,6 +808,8 @@ int register_android_view_RenderNode(JNIEnv* env) {
|
||||
jclass clazz = FindClassOrDie(env, "android/graphics/RenderNode$PositionUpdateListener");
|
||||
gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz,
|
||||
"positionChanged", "(JIIII)V");
|
||||
gPositionListener_ApplyStretchMethod =
|
||||
GetMethodIDOrDie(env, clazz, "applyStretch", "(JFFFFFFF)V");
|
||||
gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz,
|
||||
"positionLost", "(J)V");
|
||||
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
|
||||
|
||||
@@ -400,6 +400,15 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="StretchySurfaceViewActivity"
|
||||
android:label="SurfaceView/Stretchy Movement"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="com.android.test.hwui.TEST"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="GetBitmapSurfaceViewActivity"
|
||||
android:label="SurfaceView/GetBitmap with Camera source"
|
||||
android:exported="true">
|
||||
|
||||
@@ -19,8 +19,13 @@ package com.android.test.hwui;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.RecordingCanvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.RenderNode;
|
||||
import android.os.Bundle;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.ScrollView;
|
||||
@@ -38,7 +43,46 @@ public class PositionListenerActivity extends Activity {
|
||||
ProgressBar spinner = new ProgressBar(this, null, android.R.attr.progressBarStyleLarge);
|
||||
layout.addView(spinner);
|
||||
|
||||
ScrollView scrollingThing = new ScrollView(this);
|
||||
ScrollView scrollingThing = new ScrollView(this) {
|
||||
int setting = 0;
|
||||
PointF opts[] = new PointF[] {
|
||||
new PointF(0, 0),
|
||||
new PointF(0, -1f),
|
||||
new PointF(1f, 0),
|
||||
new PointF(0, 1f),
|
||||
new PointF(-1f, 0),
|
||||
new PointF(-1f, 1f),
|
||||
};
|
||||
{
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
|
||||
setting = (setting + 1) % opts.length;
|
||||
invalidate();
|
||||
}
|
||||
return super.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
RenderNode node = ((RecordingCanvas) canvas).mNode;
|
||||
PointF dir = opts[setting];
|
||||
float maxStretchAmount = 100f;
|
||||
// Although we could do this in a single call, the real one won't be - so mimic that
|
||||
if (dir.x != 0f) {
|
||||
node.stretch(0f, 0f, (float) getWidth(), (float) getHeight(),
|
||||
dir.x, 0f, maxStretchAmount);
|
||||
}
|
||||
if (dir.y != 0f) {
|
||||
node.stretch(0f, 0f, (float) getWidth(), (float) getHeight(),
|
||||
0f, dir.y, maxStretchAmount);
|
||||
}
|
||||
}
|
||||
};
|
||||
scrollingThing.addView(new MyPositionReporter(this));
|
||||
layout.addView(scrollingThing);
|
||||
|
||||
@@ -49,6 +93,11 @@ public class PositionListenerActivity extends Activity {
|
||||
RenderNode mNode;
|
||||
int mCurrentCount = 0;
|
||||
int mTranslateY = 0;
|
||||
Rect mPosition = new Rect();
|
||||
RectF mStretchArea = new RectF();
|
||||
float mStretchX = 0.0f;
|
||||
float mStretchY = 0.0f;
|
||||
float mStretchMax = 0.0f;
|
||||
|
||||
MyPositionReporter(Context c) {
|
||||
super(c);
|
||||
@@ -78,18 +127,36 @@ public class PositionListenerActivity extends Activity {
|
||||
canvas.drawRenderNode(mNode);
|
||||
}
|
||||
|
||||
void updateText() {
|
||||
setText(String.format("%d: Position %s, stretch area %s, vec %f,%f, amount %f",
|
||||
mCurrentCount, mPosition.toShortString(), mStretchArea.toShortString(),
|
||||
mStretchX, mStretchY, mStretchMax));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
|
||||
post(() -> {
|
||||
getHandler().postAtFrontOfQueue(() -> {
|
||||
mCurrentCount++;
|
||||
setText(String.format("%d: Position [%d, %d, %d, %d]", mCurrentCount,
|
||||
left, top, right, bottom));
|
||||
mPosition.set(left, top, right, bottom);
|
||||
updateText();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyStretch(long frameNumber, float left, float top, float right, float bottom,
|
||||
float vecX, float vecY, float maxStretch) {
|
||||
getHandler().postAtFrontOfQueue(() -> {
|
||||
mStretchArea.set(left, top, right, bottom);
|
||||
mStretchX = vecX;
|
||||
mStretchY = vecY;
|
||||
mStretchMax = maxStretch;
|
||||
updateText();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void positionLost(long frameNumber) {
|
||||
post(() -> {
|
||||
getHandler().postAtFrontOfQueue(() -> {
|
||||
mCurrentCount++;
|
||||
setText(mCurrentCount + " No position");
|
||||
});
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.android.test.hwui;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RecordingCanvas;
|
||||
import android.graphics.RenderNode;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceHolder.Callback;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
public class StretchySurfaceViewActivity extends Activity implements Callback {
|
||||
SurfaceView mSurfaceView;
|
||||
ObjectAnimator mAnimator;
|
||||
|
||||
class MySurfaceView extends SurfaceView {
|
||||
boolean mSlow;
|
||||
boolean mScaled;
|
||||
int mToggle = 0;
|
||||
|
||||
public MySurfaceView(Context context) {
|
||||
super(context);
|
||||
setOnClickListener(v -> {
|
||||
mToggle = (mToggle + 1) % 4;
|
||||
mSlow = (mToggle & 0x2) != 0;
|
||||
mScaled = (mToggle & 0x1) != 0;
|
||||
|
||||
mSurfaceView.setScaleX(mScaled ? 1.6f : 1f);
|
||||
mSurfaceView.setScaleY(mScaled ? 0.8f : 1f);
|
||||
|
||||
setTitle("Slow=" + mSlow + ", scaled=" + mScaled);
|
||||
invalidate();
|
||||
});
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
super.draw(canvas);
|
||||
if (mSlow) {
|
||||
try {
|
||||
Thread.sleep(16);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
|
||||
public void setMyTranslationY(float ty) {
|
||||
setTranslationY(ty);
|
||||
if (mSlow) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public float getMyTranslationY() {
|
||||
return getTranslationY();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
FrameLayout content = new FrameLayout(this) {
|
||||
{
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(Color.RED);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setStrokeWidth(10f);
|
||||
canvas.drawLine(0f, 0f, getWidth(), getHeight(), paint);
|
||||
super.onDraw(canvas);
|
||||
|
||||
RenderNode node = ((RecordingCanvas) canvas).mNode;
|
||||
node.stretch(0f, 0f, getWidth(), getHeight() / 2f, 0f, 1f, 400f);
|
||||
}
|
||||
};
|
||||
|
||||
mSurfaceView = new MySurfaceView(this);
|
||||
mSurfaceView.getHolder().addCallback(this);
|
||||
|
||||
final float density = getResources().getDisplayMetrics().density;
|
||||
int size = (int) (200 * density);
|
||||
|
||||
content.addView(mSurfaceView, new FrameLayout.LayoutParams(
|
||||
size, size, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
|
||||
mAnimator = ObjectAnimator.ofFloat(mSurfaceView, "myTranslationY",
|
||||
0, size);
|
||||
mAnimator.setRepeatMode(ObjectAnimator.REVERSE);
|
||||
mAnimator.setRepeatCount(ObjectAnimator.INFINITE);
|
||||
mAnimator.setDuration(1000);
|
||||
mAnimator.setInterpolator(new LinearInterpolator());
|
||||
setContentView(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
Canvas canvas = holder.lockCanvas();
|
||||
canvas.drawColor(Color.WHITE);
|
||||
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(Color.GREEN);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setStrokeWidth(10f);
|
||||
canvas.drawLine(0, 0, width, height, paint);
|
||||
|
||||
holder.unlockCanvasAndPost(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mAnimator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
mAnimator.pause();
|
||||
super.onPause();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user