Yazar | Wei Wei Miao
Editör | Tu Min
Üretildi | CSDN Blogu
Doğrudan konuya gidin, başarmak istediğimiz şey bir Android Müşteri İçine bir tür benzer efekt uygulayın.Örneğin, aşk şeklindeki resme tıklarsanız, küçük bir aşk kalbi oluşacak ve kaybolana kadar kıvrımlı bir şekilde yükselecektir.
Metin açıklaması ancak böyle olabilir, dinamik resme doğrudan bakalım, etkisi daha sezgiseldir.
Bu vaka kendi başıma yazılmıştır, çünkü daha önce bu Bézier eğrisini biraz anladım ve bu etkiyi tesadüfen gördüm. Çok iyi olduğunu düşündüm, bu arada bir demo yazdım ve Besse hakkında biraz bilgi edindim Er eğrisi bilgisi.
Öncelikle, bu durumun kodunu anlamak için Android özel Görünümü ile ilgili temel bilgilere sahip olmanız ve Bezier eğrileriyle ilgili bazı formül ve algoritmaları anlamanız gerekiyor. Ancak önemli değil, kodu formüle göre uygulayabildiğimiz sürece Bessel hakkında derinlemesine bir anlayışa sahip olmamıza gerek yok.
Bezier eğrisi ile ilgili bazı bilgilere bir göz atalım, aynı zamanda büyük adamın blogundan da öğrendim. Bakalım Bezier eğrisi nedir?
Bézier eğrisi veya Bézier eğrisi olarak da bilinen Bézier eğrisi, iki boyutlu grafik uygulamalarına uygulanan matematiksel bir eğridir. Genel vektör grafik yazılımı, eğriyi doğru bir şekilde çizmek için kullanır. Bezier eğrisi, çizgi parçaları ve düğümlerden oluşur. Düğümler, sürüklenebilir dayanaklardır. Çizgi parçaları, uzatılabilir lastik bantlar gibidir. Çizim aracında gördüğümüz kalem aracı, bunu yapmaktır. Bu tür bir vektör eğrisi.
Daha canlı olması için doğrudan dinamik resme bakalım.
Birinci dereceden Bezier eğrisi formülü: P0'dan P1'e ardışık noktalarla tanımlanan bir çizgi parçası
İkinci dereceden Bezier eğrisi formülü: P0-P1, P1-P2 eğrisinin tanjantından oluşan yörünge
Üçüncü dereceden Bezier eğrisi formülü:
Yukarıdaki dinamik grafikten, eğrinin hesaplama formülünü ve yol oluşum yasasını çok sezgisel olarak görebilirsiniz. Elde etmek istediğimiz etki, üçüncü dereceden Bezier eğrisi formülüdür. Her şeyden önce, eğrinin yolunu belirlemeniz gerekiyorsa, önce nokta konumunu belirlemelisiniz. Nokta konumunu bu şekilde aşağıda gösterildiği gibi belirledim:
Bu üç noktayı kullanıyorum, her iki taraf da rastgele seçilebilir. Bu durumda, eğrimiz ekranda ve oluşumu kabaca yukarıdaki dinamik resme benziyor. Ardından koda bakın:
private Point setPoint1 {
Puan noktaları = yeni Nokta {
yeni Nokta (mLoveX, mLoveY),
yeni Nokta (0, mCanvasHeight / 2),
yeni Nokta (mCanvasWidth + 20, -mLoveWidth-10),
};
dönüş noktaları;
}
private Point setPoint2 {
Puan noktaları = yeni Nokta {
yeni Nokta (mLoveX, mLoveY),
yeni Nokta (mCanvasWidth, mCanvasHeight / 2),
yeni Nokta (-mLoveWidth-20, -mLoveWidth-10),
};
dönüş noktaları;
}
Yukarıdaki kod, iki noktanın koordinatlarını başlatmak içindir, mLoveX, mLoveY, aşkımızın başlangıç konumunu temsil eder. İlk montaj noktası şekildeki mavi çizgiye karşılık gelir ve ikinci montaj noktası turuncuya karşılık gelir.
Bir sonraki kısım önemli kısım, Bezier eğri formülünü kod formuna çevirmektir.Dinamik grafiğe göre bir t değeri vardır ve aralığı evettir.Bu da çok canlıdır. T 0'dan 1'e değiştiğinde demektir Eğri çizildi. Koda bakın:
/ **
* Noktaya göre eğrinin yolu üzerindeki noktayı alın, k değişim trendi
* /
private Point deCasteljau (Point points, float k) {
final int n = points.length;
for (int i = 1; i < = n; i ++)
for (int j = 0; j < n-i; j ++) {
points.x = (int) ((1-k) * points.x + k * points.x);
points.y = (int) ((1-k) * points.y + k * points.y);
}
dönüş noktaları;
}
Az önce tanımladığımız iki noktanın seti içeri aktarılabilir, böylece k değerindeki değişikliğe göre karşılık gelen pozisyonun eğrisi üzerindeki nokta koordinatları elde edilebilir. Daha sonra, görevimiz yeni k değerini takip etmek için bir alt iş parçacığı başlatmak, k değerini 0'dan 1'e eklemek ve sonra döndürülen her nokta nesnesi tüm eğrinin koordinat dağılımıdır. Puan almak için alt iş parçacığını çalıştıran kod:
mLoveThread = new Thread (yeni Runnable {
@Override
public void run {
süre (k < 1) {
k + = 0.01;
Nokta noktası = deCasteljau (mPoints, k);
mLoveX = nokta.x;
mLoveY = nokta.y;
eğer (mLoveY < = -mLoveWidth || mLoveY > = mCanvasHeight) {
k = 1;
}
eğer (mLoveX < = -mLoveWidth || mLoveX > = mCanvasWidth) {
k = 1;
}
postInvalidate; // Eşzamansız yenileme
Deneyin {
Thread.sleep (80);
} catch (InterruptedException e) {
e.printStackTrace;
}
}
}
});
Yukarıdaki kod aracılığıyla, aşk resminin x ve y koordinatlarını alabilir ve ardından onDraw aracılığıyla çizebiliriz.
@Override
korumalı void onDraw (Canvas canvas) {
super.onDraw (tuval);
mCanvasWidth = canvas.getWidth;
mCanvasHeight = canvas.getHeight;
mLoveBitmapX = mCanvasWidth / 2-mLoveBitmapWidth / 2;
mLoveBitmapY = mCanvasHeight-2 * mLoveBitmapHeight;
drawLoveBitmap (tuval);
canvas.drawBitmap (mDefLove, mLoveX, mLoveY, mPaint);
// Rasgele çizin
canvas.drawText ("Beğen", mCanvasWidth / 2-mPaint.getTextSize, mLoveBitmapY + mLoveBitmapHeight + 100, mPaint);
canvas.drawLine (0, mLoveBitmapY + mLoveBitmapHeight + 20, mCanvasWidth, mLoveBitmapY + mLoveBitmapHeight + 20, mPaint);
}
Buradaki aşk için altı farklı resim kullandım.Aşk fonksiyonu formülünü kullanarak çizmeyi denemek istedim ama pes ettim.Hesaplama çok yavaş.Her aşk hesaplaması duraklatılmalı ve resmi değiştirmem gerekiyor. form.
Son olarak, yalnızca bu resme tıkladıktan sonra çizilen işlevden bahsediyorum. OnTouchEvent'teki tıklamanın koordinat konumunu alıyorum ve ardından tıklama konumunun aşk resminde olup olmadığına karar veriyorum. Kod şu şekildedir:
özel boole isTouchLoveArea (int touchX, int touchY) {
touchX'e dön > = mLoveBitmapX touchX < = mLoveBitmapX + mLoveBitmapWidth
touchY > mLoveBitmapY touchY < = mLoveBitmapY + mLoveBitmapHeight;
}
Sonunda tanıtılacak hiçbir şey yok, geri kalanı temelde özel Görünüm bilgisidir, biz esas olarak Dikkat Bu Bezier eğrisinin nasıl çizileceği gayet iyi, bu nedenle kodun tamamı aşağıdaki gibidir:
paket com.example.xww.my uygulama ruhsat;
android.content.Context'i içe aktarın;
ithal android.graphics.Bitmap;
android.graphics.BitmapFactory dosyasını içe aktarın;
ithal android.graphics.Canvas;
ithal android.graphics.Paint;
ithal android.graphics.Point;
ithal android.os.Build;
android.support.annotation.able dosyasını içe aktarın;
ithal android.support.annotation.RequiresApi;
ithal android.util.AttributeSet;
ithal android.view.MotionEvent;
android.view.View içe aktarın;
java.util.Random içe aktarın;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/ **
* @author xww
* @desciption: Sevdiğiniz zaman aşk yüzer ve sevginin yolu Bezier eğrisiyle çizilir
* @ : https://blog.csdn.net/smile_running
* @tarih 2019/7 / 30
* @ saat 20:59
* /
@RequiresApi (api = Build.VERSION_CODES.N)
public class LoveView, View {
özel Boya mPaint;
// Aşk resmi
özel Bitmap mLoveBitmap;
özel Bitmap mLove1;
özel Bitmap mLove2;
özel Bitmap mLove3;
özel Bitmap mLove4;
özel Bitmap mLove5;
özel Bitmap mLove6;
özel Bitmap mDefLove;
özel int mLoveWidth;
özel int mLoveX;
özel giriş mLoveY;
// Resmin x ve y koordinatları
özel int mLoveBitmapX;
özel int mLoveBitmapY;
// Resmin genişliği ve yüksekliği
özel int mLoveBitmapWidth;
özel int mLoveBitmapHeight;
// Tuval genişliği ve yüksekliği
özel int mCanvasWidth;
özel int mCanvasHeight;
// Temas noktası
özel int mTouchX;
private int mTouchY;
özel ExecutorService mExecutorService;
özel Konu mLoveThread;
// rastgele sayı
özel Rastgele mRandom;
private float k; // eğri eğimi k:
private Point mPoints; // Eğri üzerinde rastgele noktaların bir koleksiyonunu oluşturur
@Override
korumalı void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension (measureSpecWidth (widthMeasureSpec), measureSpecHeigth (heightMeasureSpec));
}
/ **
* TAM: Tam değer, yani 64dp gibi belirli bir değer
* AT_MOST: üst Görünüm ile aynı boyuta ulaşabilen maksimum değer, yani wrap_content türü
* BELİRLENMEDİ: Belirtilmedi, yani bu Görünüm sonsuz olabilir
*
* @param widthMeasureSpec genişlik değerinde iletildi
* @ dönüş genişliği değeri
* /
private int measureSpecWidth (int widthMeasureSpec) {
int mode = MeasureSpec.getMode (widthMeasureSpec);
int size = MeasureSpec.getSize (widthMeasureSpec);
dönüş modu == ÖlçüSpec.EXACTLY? boyut: Math.min (200, boyut);
}
private int measureSpecHeigth (int heightMeasureSpec) {
int mode = MeasureSpec.getMode (heightMeasureSpec);
int size = MeasureSpec.getSize (heightMeasureSpec);
dönüş modu == ÖlçüSpec.EXACTLY? boyut: Math.min (200, boyut);
}
private void init {
initPaint;
initBitmap;
mRandom = new Random;
mExecutorService = Executors.newWorkStealingPool (6);
}
özel void initBitmap {
mLoveBitmap = BitmapFactory.decodeResource (getResources, R.drawable.loveclick);
mLoveBitmap = Bitmap.createScaledBitmap (mLoveBitmap, 180, 180, false);
mLoveBitmapWidth = mLoveBitmap.getWidth;
mLoveBitmapHeight = mLoveBitmap.getHeight;
mLove1 = BitmapFactory.decodeResource (getResources, R.drawable.love1);
mLove2 = BitmapFactory.decodeResource (getResources, R.drawable.love2);
mLove3 = BitmapFactory.decodeResource (getResources, R.drawable.love3);
mLove4 = BitmapFactory.decodeResource (getResources, R.drawable.love4);
mLove5 = BitmapFactory.decodeResource (getResources, R.drawable.love5);
mLove6 = BitmapFactory.decodeResource (getResources, R.drawable.love6);
mLove1 = reSizeLove (mLove1);
mLove2 = reSizeLove (mLove2);
mLove3 = reSizeLove (mLove3);
mLove4 = reSizeLove (mLove4);
mLove5 = reSizeLove (mLove5);
mLove6 = reSizeLove (mLove6);
mDefLove = mLove1;
mLoveWidth = mLove1.getWidth;
setDefPosition;
}
private Bitmap reSizeLove (Bitmap src) {
Bitmap.createScaledBitmap (src, 160, 160, false) döndür;
}
private void initPaint {
mPaint = new Paint;
mPaint.setColor (getResources.getColor (android.R.color.holo_purple));
mPaint.setStrokeWidth (8f);
mPaint.setStyle (Paint.Style.FILL);
mPaint.setDither (true);
mPaint.setAntiAlias (doğru);
mPaint.setTextSize (45f);
}
public LoveView (Bağlam bağlamı) {
bu (bağlam);
}
public LoveView (Bağlam bağlamı, @able AttributeSet attrs) {
bu (bağlam, attrs, 0);
}
public LoveView (Bağlam bağlamı, @able AttributeSet attrs, int defStyleAttr) {
süper (bağlam, attrs, defStyleAttr);
içinde;
}
@Override
korumalı void onDraw (Canvas canvas) {
super.onDraw (tuval);
mCanvasWidth = canvas.getWidth;
mCanvasHeight = canvas.getHeight;
mLoveBitmapX = mCanvasWidth / 2-mLoveBitmapWidth / 2;
mLoveBitmapY = mCanvasHeight-2 * mLoveBitmapHeight;
drawLoveBitmap (tuval);
canvas.drawBitmap (mDefLove, mLoveX, mLoveY, mPaint);
// Rasgele çizin
canvas.drawText ("Beğen", mCanvasWidth / 2-mPaint.getTextSize, mLoveBitmapY + mLoveBitmapHeight + 100, mPaint);
canvas.drawLine (0, mLoveBitmapY + mLoveBitmapHeight + 20, mCanvasWidth, mLoveBitmapY + mLoveBitmapHeight + 20, mPaint);
}
private Point setPoint1 {
Puan noktaları = yeni Nokta {
yeni Nokta (mLoveX, mLoveY),
yeni Nokta (0, mCanvasHeight / 2),
yeni Nokta (mCanvasWidth + 20, -mLoveWidth-10),
};
dönüş noktaları;
}
private Point setPoint2 {
Puan noktaları = yeni Nokta {
yeni Nokta (mLoveX, mLoveY),
yeni Nokta (mCanvasWidth, mCanvasHeight / 2),
yeni Nokta (-mLoveWidth-20, -mLoveWidth-10),
};
dönüş noktaları;
}
private void setDefPosition {
mLoveX = mCanvasWidth / 2-mLoveWidth / 2;
mLoveY = mLoveBitmapY-80;
}
private void drawDynamicLove {
setDefPosition;
// Sevgi kalbinin tarzını ve konumunu belirleyin
int color = mRandom.nextInt (6) + 1;
mDefLove = getBitmap (renk);
k = 0; // başla
// Bezier yolunun noktalarını ekleyin
eğer (mRandom.nextInt (2) == 0) {
mPoints = setPoint1;
} Başka {
mPoints = setPoint2;
}
mLoveThread = new Thread (yeni Runnable {
@Override
public void run {
süre (k < 1) {
k + = 0.01;
Nokta noktası = deCasteljau (mPoints, k);
mLoveX = nokta.x;
mLoveY = nokta.y;
eğer (mLoveY < = -mLoveWidth || mLoveY > = mCanvasHeight) {
k = 1;
}
eğer (mLoveX < = -mLoveWidth || mLoveX > = mCanvasWidth) {
k = 1;
}
postInvalidate; // Eşzamansız yenileme
Deneyin {
Thread.sleep (80);
} catch (InterruptedException e) {
e.printStackTrace;
}
}
}
});
mExecutorService.execute (mLoveThread);
}
private Bitmap getBitmap (int color) {
anahtarı (renk) {
dava 1:
dönüş mLove1;
durum 2:
dönüş mLove2;
durum 3:
dönüş mLove3;
vaka 4:
dönüş mLove4;
vaka 5:
dönüş mLove5;
vaka 6:
dönüş mLove6;
}
dönüş;
}
private void drawLoveBitmap (Kanvas kanvas) {
canvas.drawBitmap (mLoveBitmap, mLoveBitmapX, mLoveBitmapY, mPaint);
}
/ **
* Noktaya göre eğrinin yolu üzerindeki noktayı alın, k değişim trendi
* /
private Point deCasteljau (Point points, float k) {
final int n = points.length;
for (int i = 1; i < = n; i ++)
for (int j = 0; j < n-i; j ++) {
points.x = (int) ((1-k) * points.x + k * points.x);
points.y = (int) ((1-k) * points.y + k * points.y);
}
dönüş noktaları;
}
@Override
public boolean onTouchEvent (MotionEvent olayı) {
mTouchX = (int) event.getX;
mTouchY = (int) event.getY;
switch (event.getAction) {
case MotionEvent.ACTION_DOWN:
eğer (isTouchLoveArea (mTouchX, mTouchY)) {
drawDynamicLove;
}
kırmak;
case MotionEvent.ACTION_UP:
kırmak;
}
return super.onTouchEvent (olay);
}
özel boole isTouchLoveArea (int touchX, int touchY) {
touchX'e dön > = mLoveBitmapX touchX < = mLoveBitmapX + mLoveBitmapWidth
touchY > mLoveBitmapY touchY < = mLoveBitmapY + mLoveBitmapHeight;
}
}
Bu, tüm efektin kod diyagramıdır. Activity_main'e koyun ve etkisini görmek için çalıştırın.
Sorumluluk Reddi: Bu makale, CSDN blog makalelerinden bir seçkidir ve telif hakkı yazara aittir. Eser sahibi: Mew Whiskers
Orijinal: https://blog.csdn.net/smile_Running/article/details/98170645
SON