Android自定義View——從零開始實現雪花飄落效果
前言:轉眼已是十一月下旬了,天氣慢慢轉冷,不知道北方是不是已經開始下雪了呢?本期教程我們就順應季節主題,一起來實現 雪花飄落的效果吧。本篇效果思路參考自國外大神的Android實現雪花飛舞效果,並在此基礎上實現進一步的封裝和功能擴充套件
本篇只著重於思路和實現步驟,裡面用到的一些知識原理不會非常細地拿來講,如果有不清楚的api或方法可以在網上搜下相應的資料,肯定有大神講得非常清楚的,我這就不獻醜了。本著認真負責的精神我會把相關知識的博文連結也貼出來(其實就是懶不想寫那麼多哈哈),大家可以自行傳送。為了照顧第一次閱讀系列部落格的小夥伴,本篇會出現一些在之前系列部落格就講過的內容,看過的童鞋自行跳過該段即可
國際慣例,先上效果圖
目錄
- 繪製一個迴圈下落的“雪球”
- 封裝下落物體物件
- 擴充套件一:增加匯入Drawable資源的構造方法和設定物體大小的介面
- 擴充套件一:擴充套件二:實現雪花“大小不一”、“快慢有別”的效果
- 擴充套件三:引入“風”的概念
繪製一個迴圈下落的“雪球”
我們先從最簡單的部分做起,自定義View中實現迴圈動畫的方法有很多,最簡單直接的當然是用Animation類去實現,但考慮到無論是雪花、雪球亦或是雨滴什麼的,每個獨立的個體都有自己的起點、速度和方向等等,其下落的過程會出現很多隨機的因素,實現這種非規律的動畫Animation
public class FallingView extends View {
private Context mContext;
private AttributeSet mAttrs;
private int viewWidth;
private int viewHeight;
private static final int defaultWidth = 600 ;//預設寬度
private static final int defaultHeight = 1000;//預設高度
private static final int intervalTime = 5;//重繪間隔時間
private Paint testPaint;
private int snowY;
public FallingView(Context context) {
super(context);
mContext = context;
init();
}
public FallingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mContext = context;
mAttrs = attrs;
init();
}
private void init(){
testPaint = new Paint();
testPaint.setColor(Color.WHITE);
testPaint.setStyle(Paint.Style.FILL);
snowY = 0;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = measureSize(defaultHeight, heightMeasureSpec);
int width = measureSize(defaultWidth, widthMeasureSpec);
setMeasuredDimension(width, height);
viewWidth = width;
viewHeight = height;
}
private int measureSize(int defaultSize,int measureSpec) {
int result = defaultSize;
int specMode = View.MeasureSpec.getMode(measureSpec);
int specSize = View.MeasureSpec.getSize(measureSpec);
if (specMode == View.MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == View.MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
return result;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(100,snowY,25,testPaint);
getHandler().postDelayed(runnable, intervalTime);//間隔一段時間再進行重繪
}
// 重繪執行緒
private Runnable runnable = new Runnable() {
@Override
public void run() {
snowY += 15;
if(snowY>viewHeight){//超出螢幕則重置雪球位置
snowY = 0;
}
invalidate();
}
};
}
效果如圖
在上述程式碼中View基本的框架我們已經搭好了,思路其實很簡單,我們需要做僅僅是在每次重繪之前更新做下落運動的物體的位置即可
封裝下落物體物件
要實現大雪紛飛的效果,很明顯只有一個雪球是不夠的,而且雪也不能只有雪球一個形狀,我們希望可以自定義雪的樣式,甚至不侷限於下雪,還可以下雨、下金幣等等,因此我們要對下落的物體進行封裝。為了以後物體類對外方法程式碼的可讀性,這裡我們採用Builder設計模式來構建物體物件類,新建FallObject
public class FallObject {
private int initX;
private int initY;
private Random random;
private int parentWidth;//父容器寬度
private int parentHeight;//父容器高度
private float objectWidth;//下落物體寬度
private float objectHeight;//下落物體高度
public int initSpeed;//初始下降速度
public float presentX;//當前位置X座標
public float presentY;//當前位置Y座標
public float presentSpeed;//當前下降速度
private Bitmap bitmap;
public Builder builder;
private static final int defaultSpeed = 10;//預設下降速度
public FallObject(Builder builder, int parentWidth, int parentHeight){
random = new Random();
this.parentWidth = parentWidth;
this.parentHeight = parentHeight;
initX = random.nextInt(parentWidth);//隨機物體的X座標
initY = random.nextInt(parentHeight)- parentHeight;//隨機物體的Y座標,並讓物體一開始從螢幕頂部下落
presentX = initX;
presentY = initY;
initSpeed = builder.initSpeed;
presentSpeed = initSpeed;
bitmap = builder.bitmap;
objectWidth = bitmap.getWidth();
objectHeight = bitmap.getHeight();
}
private FallObject(Builder builder) {
this.builder = builder;
initSpeed = builder.initSpeed;
bitmap = builder.bitmap;
}
public static final class Builder {
private int initSpeed;
private Bitmap bitmap;
public Builder(Bitmap bitmap) {
this.initSpeed = defaultSpeed;
this.bitmap = bitmap;
}
/**
* 設定物體的初始下落速度
* @param speed
* @return
*/
public Builder setSpeed(int speed) {
this.initSpeed = speed;
return this;
}
public FallObject build() {
return new FallObject(this);
}
}
/**
* 繪製物體物件
* @param canvas
*/
public void drawObject(Canvas canvas){
moveObject();
canvas.drawBitmap(bitmap,presentX,presentY,null);
}
/**
* 移動物體物件
*/
private void moveObject(){
moveY();
if(presentY>parentHeight){
reset();
}
}
/**
* Y軸上的移動邏輯
*/
private void moveY(){
presentY += presentSpeed;
}
/**
* 重置object位置
*/
private void reset(){
presentY = -objectHeight;
presentSpeed = initSpeed;
}
}
FallingView中相應地設定新增物體的方法
public class FallingView extends View {
//省略部分程式碼...
private List<FallObject> fallObjects;
private void init(){
fallObjects = new ArrayList<>();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(fallObjects.size()>0){
for (int i=0;i<fallObjects.size();i++) {
//然後進行繪製
fallObjects.get(i).drawObject(canvas);
}
// 隔一段時間重繪一次, 動畫效果
getHandler().postDelayed(runnable, intervalTime);
}
}
// 重繪執行緒
private Runnable runnable = new Runnable() {
@Override
public void run() {
invalidate();
}
};
/**
* 向View新增下落物體物件
* @param fallObject 下落物體物件
* @param num
*/
public void addFallObject(final FallObject fallObject, final int num) {
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
for (int i = 0; i < num; i++) {
FallObject newFallObject = new FallObject(fallObject.builder,viewWidth,viewHeight);
fallObjects.add(newFallObject);
}
invalidate();
return true;
}
});
}
}
在Activity中向FallingView新增一些物體看看效果
//繪製雪球bitmap
snowPaint = new Paint();
snowPaint.setColor(Color.WHITE);
snowPaint.setStyle(Paint.Style.FILL);
bitmap = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
bitmapCanvas.drawCircle(25,25,25,snowPaint);
//初始化一個雪球樣式的fallObject
FallObject.Builder builder = new FallObject.Builder(bitmap);
FallObject fallObject = builder
.setSpeed(10)
.build();
fallingView = (FallingView) findViewById(R.id.fallingView);
fallingView.addFallObject(fallObject,50);//新增50個雪球物件
效果如圖
到這裡我們完成了一個最基礎的下落物體類,下面開始擴充套件功能和效果
擴充套件一:增加匯入Drawable資源的構造方法和設定物體大小的介面
我們之前的FallObject類中Builder只支援bitmap的匯入,很多時候我們的圖片樣式都是從drawable資原始檔夾中獲取的,每次都要將drawable轉成bitmap是件很麻煩的事,因此我們要在FallObject類中封裝drawable資源匯入的構造方法,修改FallObject
public static final class Builder {
//省略部分程式碼...
public Builder(Bitmap bitmap) {
this.initSpeed = defaultSpeed;
this.bitmap = bitmap;
}
public Builder(Drawable drawable) {
this.initSpeed = defaultSpeed;
this.bitmap = drawableToBitmap(drawable);
}
}
/**
* drawable圖片資源轉bitmap
* @param drawable
* @return
*/
public static Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap = Bitmap.createBitmap(
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
drawable.draw(canvas);
return bitmap;
}
有了drawable資源匯入的構造方法,肯定需要配套改變FallObject圖片樣式大小的介面,依然是在FallObject的Builder中擴充套件相應的介面
public static final class Builder {
//省略部分程式碼...
public Builder setSize(int w, int h){
this.bitmap = changeBitmapSize(this.bitmap,w,h);
return this;
}
}
/**
* 改變bitmap的大小
* @param bitmap 目標bitmap
* @param newW 目標寬度
* @param newH 目標高度
* @return
*/
public static Bitmap changeBitmapSize(Bitmap bitmap, int newW, int newH) {
int oldW = bitmap.getWidth();
int oldH = bitmap.getHeight();
// 計算縮放比例
float scaleWidth = ((float) newW) / oldW;
float scaleHeight = ((float) newH) / oldH;
// 取得想要縮放的matrix引數
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
// 得到新的圖片
bitmap = Bitmap.createBitmap(bitmap, 0, 0, oldW, oldH, matrix, true);
return bitmap;
}
在Activity中初始化下落物體樣式時我們就可以匯入drawable資源和設定物體大小了(圖片資源我是在阿里圖示庫下載的)
FallObject.Builder builder = new FallObject.Builder(getResources().getDrawable(R.drawable.ic_snow));
FallObject fallObject = builder
.setSpeed(10)
.setSize(50,50)
.build();
來看下效果
擴充套件二:實現雪花“大小不一”、“快慢有別”的效果
之前我們通過匯入drawable資源的方法讓螢幕“下起了雪花”,但雪花個個都一樣大小,下落速度也都完全一致,這顯得十分的單調,看起來一點也不像現實中的下雪場景。因此我們需要利用隨機數實現雪花“大小不一”、“快慢有別”的效果,修改FallObject
public class FallObject {
//省略部分程式碼...
private boolean isSpeedRandom;//物體初始下降速度比例是否隨機
private boolean isSizeRandom;//物體初始大小比例是否隨機
public FallObject(Builder builder, int parentWidth, int parentHeight){
//省略部分程式碼...
this.builder = builder;
isSpeedRandom = builder.isSpeedRandom;
isSizeRandom = builder.isSizeRandom;
initSpeed = builder.initSpeed;
randomSpeed();
randomSize();
}
private FallObject(Builder builder) {
//省略部分程式碼...
isSpeedRandom = builder.isSpeedRandom;
isSizeRandom = builder.isSizeRandom;
}
public static final class Builder {
//省略部分程式碼...
private boolean isSpeedRandom;
private boolean isSizeRandom;
public Builder(Bitmap bitmap) {
//省略部分程式碼...
this.isSpeedRandom = false;
this.isSizeRandom = false;
}
public Builder(Drawable drawable) {
//省略部分程式碼...
this.isSpeedRandom = false;
this.isSizeRandom = false;
}
/**
* 設定物體的初始下落速度
* @param speed
* @return
*/
public Builder setSpeed(int speed) {
this.initSpeed = speed;
return this;
}
/**
* 設定物體的初始下落速度
* @param speed
* @param isRandomSpeed 物體初始下降速度比例是否隨機
* @return
*/
public Builder setSpeed(int speed,boolean isRandomSpeed) {
this.initSpeed = speed;
this.isSpeedRandom = isRandomSpeed;
return this;
}
/**
* 設定物體大小
* @param w
* @param h
* @return
*/
public Builder setSize(int w, int h){
this.bitmap = changeBitmapSize(this.bitmap,w,h);
return this;
}
/**
* 設定物體大小
* @param w
* @param h
* @param isRandomSize 物體初始大小比例是否隨機
* @return
*/
public Builder setSize(int w, int h, boolean isRandomSize){
this.bitmap = changeBitmapSize(this.bitmap,w,h);
this.isSizeRandom = isRandomSize;
return this;
}
}
/**
* 重置object位置
*/
private void reset(){
presentY = -objectHeight;
randomSpeed();//記得重置時速度也一起重置,這樣效果會好很多
}
/**
* 隨機物體初始下落速度
*/
private void randomSpeed(){
if(isSpeedRandom){
presentSpeed = (float)((random.nextInt(3)+1)*0.1+1)* initSpeed;//這些隨機數大家可以按自己的需要進行調整
}else {
presentSpeed = initSpeed;
}
}
/**
* 隨機物體初始大小比例
*/
private void randomSize(){
if(isSizeRandom){
float r = (random.nextInt(10)+1)*0.1f;
float rW = r * builder.bitmap.getWidth();
float rH = r * builder.bitmap.getHeight();
bitmap = changeBitmapSize(builder.bitmap,(int)rW,(int)rH);
}else {
bitmap = builder.bitmap;
}
objectWidth = bitmap.getWidth();
objectHeight = bitmap.getHeight();
}
}
在Activity中設定相應引數即可
FallObject.Builder builder = new FallObject.Builder(getResources().getDrawable(R.drawable.ic_snow));
FallObject fallObject = builder
.setSpeed(10,true)
.setSize(50,50,true)
.build();
效果如圖,是不是看起來感覺好多了๑乛◡乛๑
擴充套件三:引入“風”的概念
“風”其實是一種比喻,實際上要做的是讓雪花除了做下落運動外,還會橫向移動,也就是說我們要模擬出雪花在風中亂舞的效果。為了讓雪花在X軸上的位移不顯得鬼畜(大家可以直接隨機增減x座標值就知道為什麼是鬼畜了哈哈),我們採用正弦函式來獲取X軸上的位移距離,如圖所示
正弦函式曲線見下圖
我們選取-π到π這段曲線,可以看出角的弧度在為π/2時正弦值最大(-π/2時最小),因此我們在計算角度時還需要考慮其極限值。同時,因為我們添加了橫向的移動,所以判斷邊界時要記得判定最左和最右的邊界,修改FallObject
public class FallObject {
//省略部分程式碼...
public int initSpeed;//初始下降速度
public int initWindLevel;//初始風力等級
private float angle;//物體下落角度
private boolean isWindRandom;//物體初始風向和風力大小比例是否隨機
private boolean isWindChange;//物體下落過程中風向和風力是否產生隨機變化
private static final int defaultWindLevel = 0;//預設風力等級
private static final int defaultWindSpeed = 10;//預設單位風速
private static final float HALF_PI = (float) Math.PI / 2;//π/2
public FallObject(Builder builder, int parentWidth, int parentHeight){
//省略部分程式碼...
isWindRandom = builder.isWindRandom;
isWindChange = builder.isWindChange;
initSpeed = builder.initSpeed;
randomSpeed();
randomSize();
randomWind();
}
private FallObject(Builder builder) {
//省略部分程式碼...
isWindRandom = builder.isWindRandom;
isWindChange = builder.isWindChange;
}
public static final class Builder {
//省略部分程式碼...
private boolean isWindRandom;
private boolean isWindChange;
public Builder(Bitmap bitmap) {
//省略部分程式碼...
this.isWindRandom = false;
this.isWindChange = false;
}
public Builder(Drawable drawable) {
//省略部分程式碼...
this.isWindRandom = false;
this.isWindChange = false;
}
/**
* 設定風力等級、方向以及隨機因素
* @param level 風力等級(絕對值為 5 時效果會比較好),為正時風從左向右吹(物體向X軸正方向偏移),為負時則相反
* @param isWindRandom 物體初始風向和風力大小比例是否隨機
* @param isWindChange 在物體下落過程中風的風向和風力是否會產生隨機變化
* @return
*/
public Builder setWind(int level,boolean isWindRandom,boolean isWindChange){
this.initWindLevel = level;
this.isWindRandom = isWindRandom;
this.isWindChange = isWindChange;
return this;
}
}
/**
* 移動物體物件
*/
private void moveObject(){
moveX();
moveY();
if(presentY>parentHeight || presentX<-bitmap.getWidth() || presentX>parentWidth+bitmap.getWidth()){
reset();
}
}
/**
* X軸上的移動邏輯
*/
private void moveX(){
presentX += defaultWindSpeed * Math.sin(angle);
if(isWindChange){
angle += (float) (random.nextBoolean()?-1:1) * Math.random() * 0.0025;
}
}
/**
* 重置object位置
*/
private void reset(){
presentY = -objectHeight;
randomSpeed();//記得重置時速度也一起重置,這樣效果會好很多
randomWind();//記得重置一下初始角度,不然雪花會越下越少(因為角度累加會讓雪花越下越偏)
}
/**
* 隨機風的風向和風力大小比例,即隨機物體初始下落角度
*/
private void randomWind(){
if(isWindRandom){
angle = (float) ((random.nextBoolean()?-1:1) * Math.random() * initWindLevel /50);
}else {
angle = (float) initWindLevel /50;
}
//限制angle的最大最小值
if(angle>HALF_PI){
angle = HALF_PI;
}else if(angle<-HALF_PI){
angle = -HALF_PI;
}
}
}
在Activity中呼叫新增加的介面
FallObject.Builder builder = new FallObject.Builder(getResources().getDrawable(R.drawable.ic_snow));
FallObject fallObject = builder
.setSpeed(7,true)
.setSize(50,50,true)
.setWind(5,true,true)
.build();
效果如圖
至此本篇教程到此結束,如果大家看了感覺還不錯麻煩點個贊,你們的支援是我最大的動力~
相關推薦
Android自定義View——從零開始實現雪花飄落效果
前言:轉眼已是十一月下旬了,天氣慢慢轉冷,不知道北方是不是已經開始下雪了呢?本期教程我們就順應季節主題,一起來實現 雪花飄落的效果吧。本篇效果思路參考自國外大神的Android實現雪花飛舞效果,並在此基礎上實現進一步的封裝和功能擴充套件 本篇只著重於思路和
Android自定義View——從零開始實現水波浪進度框
前言:相信同行們都知道,我們程式設計師有一種痛,叫做別人的程式碼。讀懂別人的程式碼很重要的一點就是要抓住作者的思路,有了思路才能將過程推匯出來,否則腦闊會疼。為己為人,本系列教程部落格,我都會將自己實現的思路寫下來,帶大家一步步從零開始實現我們想要的效果
Android自定義View——從零開始實現圓形進度條
前言:以前老是用別人造的輪子,知其然不知其所以然,有時看懂了別人寫的過多幾個月又忘了,遂來開個坑把一步步實現和思路寫下來,弄成一個系列。由於上班時間不多,爭取一週擼個一到兩篇出來 本篇只著重於思路和實現步驟,裡面用到的一些知識原理不會非常細地拿來講,如
Android 自定義View(繼承原生元件)實現拖動移位效果
自定義View實現拖拽移位效果 通過繼承GridLayout實現的拖拽移位效果 首先建立Class類繼承GridLayout並重寫前三個構造方法 public class MyGridlayout extends GridLayout implement
Android 自定義View(繼承原生元件)實現拖動移位效果
自定義View實現拖拽移位效果 通過繼承GridLayout實現的拖拽移位效果 首先建立Class類繼承GridLayout並重寫前三個構造方法 public class MyGridlayout extends GridLayout implements
android 自定義View 儀表盤 DashboardView 的實現
有天上班,老闆突然扔給我一張圖, 說:這個東西能不能做一下。 我說應該可以。然後老闆那就沒有下文了,我想既然問了,那我就抽空做一下。 當我做出來的時候去找老闆,我說上次你給我發的那個圖,我已經做出來了,您要不要看一下。 老闆說,不用了,不需要了。
Android自定義View初體驗,實現圓形TextView的三種方式
自定義view對我來說一直是比較恐懼的,但是萬事開頭難,今天總結一下自己實現圓形TextView的三種方式。 首先來說一下自定義view的三種方式: 一,自繪控制元件: 自繪控制元件就是說介面展示的內容就是我們在ondraw()方法中繪製出來的,繼承Vie
Android自定義view-高仿小米視訊載入動畫效果
*本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出 1、概述 前幾日出差,每晚回到酒店的時候,睡前打發時間就是拿起自己的小米手機擼劇,酒店的wifi網路實在太差,眼睜睜的看著小米視訊的載入動畫一直拼命的loading中,正好最近一直在看
從零開始學Android自定義View之動畫系列——屬性動畫(3)
屬性動畫對補間動畫進行了很大幅度的改進,之前補間動畫可以做到的屬性動畫也能做到,補間動畫做不到的現在屬性動畫也可以做到了。因此,今天我們就來學習一下屬性動畫的高階用法,看看如何實現一些補間動畫所無法實現的功能。 ValueAnimator的高階用法 補間
Android自定義 view之圖片裁剪從設計到實現
android圖片剪下是常用的功能,因為部落格開發的是SDK不涉及到activity,所以就需要自定義裁剪功能,參閱了網上的大部分資料後,在github上一個封裝好的裁剪庫cropper,正好符合要求,本著拿來主義的思想,直接把原始碼clone嵌入到專案裡,然後
Android -- 自定義view實現keep歡迎頁倒計時效果
super onfinish -m use new getc awt ttr alt 1,最近打開keep的app的時候,發現它的歡迎頁面的倒計時效果還不錯,所以打算自己來寫寫,然後就有了這篇文章。 2,還是老規矩,先看一下我們今天實現的效果 相較於我們常見的倒計時
Android自定義View——實現水波紋效果類似剩余流量球
string 三個點 pre ber block span 初始化 move 理解 最近突然手癢就想搞個貝塞爾曲線做個水波紋效果玩玩,終於功夫不負有心人最後實現了想要的效果,一起來看下吧: 效果圖鎮樓 一:先一步一步來分解一下實現的過程 需要繪制一個正弦曲線(sin
Android 自定義View-----流式佈局(粗糙實現)
//首先檢視一下佈局介面吧 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app
Android自定義view實現圖片選色器
https://www.jb51.net/article/141336.htm 這篇文章主要為大家詳細介紹了Android自定義view實現圖片選色器,具有一定的參考價值,感興趣的小夥伴們可以參考一下 簡介 本文介紹該自定義view的使用及實現的方法,主要實現以下幾個功能: - 選取
Android :自定義view實現簡易的轉盤
直接先上效果圖 xml裡面的程式碼 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android自定義View之仿通訊錄側邊欄滑動,實現A-Z字母檢索
我們的手機通訊錄一般都有這樣的效果,如下圖: OK,這種效果大家都見得多了,基本上所有的android手機通訊錄都有這樣的效果。那我們今天就來看看這個效果該怎麼實現。 一.概述 1.頁面功能分析 整體上來說,左邊是一個ListView,右邊是一個自定義View,但
Android 自定義View實現拖拽效果
騰訊QQ有那種紅點拖動效果,今天就來實現一個簡單的自定義View拖動效果,再回到原處,並非完全仿QQ紅點拖動 先來看一下效果圖 簡單說一下實現步驟 1.建立一個類繼承View 2.繪製出一個
Android 自定義View小例項-實現繪製打折標籤
前言 許多商城APP都會有商品打折的需求,而為文字新增下劃線直接設定style就可以完成,我們在這裡說的如下圖,也就是我們demo實現的效果圖。 1. 選取自定義View的方法 我們都知道自定義View有多種方式,比如繼承自View、ViewGroup或者繼承自現有的View子
Android自定義View實現類似車來了軌跡圖
總體分析下:水平方向recyclewview,item包含定位點,站臺位置和站臺名稱。 下面看實現: 1.繼承framelayout,實現構造方法: public class BusStopPlateView extends FrameLayout { ... public
Android 自定義View實現圓形環繞效果
之前專案中需要實現一個四周環繞中心圓形頭像的效果,感覺還是自定義比較方便,於是就自己封裝了一個控制元件去實現。先貼張圖顯示最終效果。 首先自定義一個View繼承自LinearLayout,通過動態新增childView的方式將子控制元件新增到View中。思路是先新增中間圓形頭像