以一個(gè)著色游戲展開(kāi)講解Android中區(qū)域圖像填色的方法
一、著色游戲概述
近期群里偶然看到一哥們?cè)谌豪锪牟灰?guī)則圖像填充什么四聯(lián)通、八聯(lián)通什么的,就本身好學(xué)務(wù)實(shí)的態(tài)度去查閱了相關(guān)資料。對(duì)于這類著色的資料,最好的就是去搜索些相關(guān)app,根據(jù)我的觀察呢,不規(guī)則圖像填充在著色游戲里面應(yīng)用居多,不過(guò)大致可以分為兩種:
- 基于層的的填充
- 基于邊界的填充
那么針對(duì)上述兩種,我們會(huì)通過(guò)兩篇博文來(lái)講解,本篇就是敘述基于層的填充方式,那么什么基于層的填充方式呢?其實(shí)就是一張圖實(shí)際上是由多個(gè)層組成的,每個(gè)層顯示部分圖像(無(wú)圖像部分為透明),多層疊加后形成一張完整的圖案,圖層間是疊加的關(guān)系,類似下圖。

相信大家如果學(xué)過(guò)PS,對(duì)上述肯定再了解不過(guò)了。比如你要繪制一個(gè)天空,你可以最底層去繪制藍(lán)天,在上層繪制白云,再上層會(huì)執(zhí)行小鳥(niǎo)。然后三層疊加以后就是一副小鳥(niǎo)在天空翱翔的圖了。
二、效果與分析
好了,接下來(lái)看下今天的效果。

ok,可以看到一個(gè)簡(jiǎn)單的著色效果,其實(shí)原理很簡(jiǎn)單,首先呢,該圖實(shí)際上是由7層組成:
例如下圖。

那么如果我們需要給這幅圖的某個(gè)位置著色,實(shí)際上是給某一層的非透明區(qū)域著色。實(shí)際上就轉(zhuǎn)化為:
用戶點(diǎn)擊的(x,y)-> 判斷落在哪一層的非透明區(qū)域 -> 然后給該層非透明區(qū)域著色。
ok,這樣原理就敘述清楚了,實(shí)際上也是非常的簡(jiǎn)單,基于該原理,我們可以自定義一個(gè)View,然后一幅一幅去繪制圖層,最后按照上述步驟去編寫(xiě)代碼。不過(guò),我們還有可以偷懶的地方,其實(shí)沒(méi)必要我們自己去一個(gè)圖層一個(gè)圖層的繪制,我們可以利用Drawable去完成圖層疊加的工作,我們有一類Drawable叫做LayerDrawable,對(duì)應(yīng)的xml為layer-list,我們可以通過(guò)使用LayerDrawable極大的簡(jiǎn)化我們的工作。
三、編碼與實(shí)現(xiàn)
上述已經(jīng)描述很清楚了,我再給大家細(xì)化一下:
layer-list中去定義我們的drawable
然后把該drawable作為我們View的背景
復(fù)寫(xiě)onTouchEvent方法
判斷用戶點(diǎn)擊的坐標(biāo)落在哪一層的非透明位置,改變?cè)搶臃峭该鲄^(qū)域顏色
(一)layer-list
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/eel_mask1"/> <item android:drawable="@drawable/eel_mask2"/> <item android:drawable="@drawable/eel_mask3"/> <item android:drawable="@drawable/eel_mask4"/> <item android:drawable="@drawable/eel_mask5"/> <item android:drawable="@drawable/eel_mask6"/> <item android:drawable="@drawable/eel_mask7"/> </layer-list>
ok,這樣我們的drawable就ok了~~沒(méi)撒說(shuō)的,不過(guò)layer-list可以做很多事情,大家可以關(guān)注下。
(二)View代碼
package com.zhy.colour_app_01;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.util.Random;
/**
* Created by zhy on 15/5/14.
*/
public class ColourImageBaseLayerView extends View
{
private LayerDrawable mDrawables;
public ColourImageBaseLayerView(Context context, AttributeSet attrs)
{
super(context, attrs);
mDrawables = (LayerDrawable) getBackground();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
setMeasuredDimension(mDrawables.getIntrinsicWidth(), mDrawables.getIntrinsicHeight());
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
final float x = event.getX();
final float y = event.getY();
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
Drawable drawable = findDrawable(x, y);
if (drawable != null)
drawable.setColorFilter(randomColor(), PorterDuff.Mode.SRC_IN);
}
return super.onTouchEvent(event);
}
private int randomColor()
{
Random random = new Random();
int color = Color.argb(255, random.nextInt(256), random.nextInt(256), random.nextInt(256));
return color;
}
private Drawable findDrawable(float x, float y)
{
final int numberOfLayers = mDrawables.getNumberOfLayers();
Drawable drawable = null;
Bitmap bitmap = null;
for (int i = numberOfLayers - 1; i >= 0; i--)
{
drawable = mDrawables.getDrawable(i);
bitmap = ((BitmapDrawable) drawable).getBitmap();
try
{
int pixel = bitmap.getPixel((int) x, (int) y);
if (pixel == Color.TRANSPARENT)
{
continue;
}
} catch (Exception e)
{
continue;
}
return drawable;
}
return null;
}
}
ok,代碼也比較簡(jiǎn)單,首先我們把drawable作為view的背景,然后在構(gòu)造中獲取drawable(LayerDrawable)。接下來(lái)復(fù)寫(xiě)onTouchEvent,捕獲用戶點(diǎn)擊的(x,y),根據(jù)(x,y)去找出當(dāng)前點(diǎn)擊的是哪一層(必須點(diǎn)擊在非透明區(qū)域),最后通過(guò)設(shè)置setColorFilter去改變顏色即可~很easy吧最后貼下布局文件:
(三)布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<com.zhy.colour_app_01.ColourImageBaseLayerView
android:background="@drawable/eel"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:layout_height="match_parent"/>
</RelativeLayout>
四、邊界的填充
1.圖像的填充有2種經(jīng)典算法。
一種是種子填充法。種子填充法理論上能夠填充任意區(qū)域和圖形,但是這種算法存在大量的反復(fù)入棧和大規(guī)模的遞歸,降低了填充效率。
另一種是掃描線填充法。
注意:實(shí)際上圖像填充的算法還是很多的,有興趣可以去Google學(xué)術(shù)上去搜一搜。
ok,下面先看看效果圖:

ok,可以看到這樣的顏色填充比上一篇的基于層的在素材的準(zhǔn)備上要easy 很多~~~
2.原理分析
首先我們簡(jiǎn)述下原理,我們?cè)邳c(diǎn)擊的時(shí)候拿到點(diǎn)擊點(diǎn)的”顏色”,然后按照我們選擇的算法進(jìn)行填色即可。
算法1:種子填充法,四聯(lián)通/八聯(lián)通
算法簡(jiǎn)介:假設(shè)要將某個(gè)區(qū)域填充成紅色。
從用戶點(diǎn)擊點(diǎn)的像素開(kāi)始,上下左右(八聯(lián)通還有左上,左下,右上,右下)去判斷顏色,如果四個(gè)方向上的顏色與當(dāng)前點(diǎn)擊點(diǎn)的像素一致,則改變顏色至目標(biāo)色。然后繼續(xù)上述這個(gè)過(guò)程。
ok,可以看到這是一個(gè)遞歸的過(guò)程,1個(gè)點(diǎn)到4個(gè),4個(gè)到16個(gè)不斷的去延伸。如果按照這種算法,你會(huì)寫(xiě)出類似這樣的代碼:
/**
* @param pixels 像素?cái)?shù)組
* @param w 寬度
* @param h 高度
* @param pixel 當(dāng)前點(diǎn)的顏色
* @param newColor 填充色
* @param i 橫坐標(biāo)
* @param j 縱坐標(biāo)
*/
private void fillColor01(int[] pixels, int w, int h, int pixel, int newColor, int i, int j)
{
int index = j * w + i;
if (pixels[index] != pixel || i >= w || i < 0 || j < 0 || j >= h)
return;
pixels[index] = newColor;
//上
fillColor01(pixels, w, h, pixel, newColor, i, j - 1);
//右
fillColor01(pixels, w, h, pixel, newColor, i + 1, j);
//下
fillColor01(pixels, w, h, pixel, newColor, i, j + 1);
//左
fillColor01(pixels, w, h, pixel, newColor, i - 1, j);
}
代碼很簡(jiǎn)單,但是如果你去運(yùn)行,會(huì)發(fā)生StackOverflowException異常,這個(gè)異常主要是因?yàn)榇罅康倪f歸造成的。雖然簡(jiǎn)單,但是在移動(dòng)設(shè)備上使用該方法不行。
于是,我就想,這個(gè)方法不是遞歸深度過(guò)多么,那么我可以使用一個(gè)Stack去存像素點(diǎn),減少遞歸的深度和次數(shù),于是我把代碼改成如下的方式:
/**
* @param pixels 像素?cái)?shù)組
* @param w 寬度
* @param h 高度
* @param pixel 當(dāng)前點(diǎn)的顏色
* @param newColor 填充色
* @param i 橫坐標(biāo)
* @param j 縱坐標(biāo)
*/
private void fillColor(int[] pixels, int w, int h, int pixel, int newColor, int i, int j)
{
mStacks.push(new Point(i, j));
while (!mStacks.isEmpty())
{
Point seed = mStacks.pop();
Log.e("TAG", "seed = " + seed.x + " , seed = " + seed.y);
int index = seed.y * w + seed.x;
pixels[index] = newColor;
if (seed.y > 0)
{
int top = index - w;
if (pixels[top] == pixel)
{
mStacks.push(new Point(seed.x, seed.y - 1));
}
}
if (seed.y < h - 1)
{
int bottom = index + w;
if (pixels[bottom] == pixel)
{
mStacks.push(new Point(seed.x, seed.y + 1));
}
}
if (seed.x > 0)
{
int left = index - 1;
if (pixels[left] == pixel)
{
mStacks.push(new Point(seed.x - 1, seed.y));
}
}
if (seed.x < w - 1)
{
int right = index + 1;
if (pixels[right] == pixel)
{
mStacks.push(new Point(seed.x + 1, seed.y));
}
}
}
}
方法的思想也比較簡(jiǎn)單,將當(dāng)前像素點(diǎn)入棧,然后出棧著色,接下來(lái)分別判斷四個(gè)方向的,如果符合條件也進(jìn)行入棧(只要棧不為空持續(xù)運(yùn)行)。ok,這個(gè)方法我也嘗試跑了下,恩,這次不會(huì)報(bào)錯(cuò)了,但是速度特別的慢~~~~慢得我是不可接受的。(有興趣可以嘗試,記得如果ANR,點(diǎn)擊等待)。
這樣來(lái)看,第一種算法,我們是不考慮了,沒(méi)有辦法使用,主要原因是假設(shè)對(duì)于矩形同色區(qū)域,都是需要填充的,而算法一依然是各種入棧。于是考慮第二種算法
掃描線填充法
詳細(xì)可參考 掃描線種子填充算法的解析和掃描線種子填充算法。
算法思想:
初始化一個(gè)空的棧用于存放種子點(diǎn),將種子點(diǎn)(x, y)入棧;
判斷棧是否為空,如果棧為空則結(jié)束算法,否則取出棧頂元素作為當(dāng)前掃描線的種子點(diǎn)(x, y),y是當(dāng)前的掃描線;
從種子點(diǎn)(x, y)出發(fā),沿當(dāng)前掃描線向左、右兩個(gè)方向填充,直到邊界。分別標(biāo)記區(qū)段的左、右端點(diǎn)坐標(biāo)為xLeft和xRight;
分別檢查與當(dāng)前掃描線相鄰的y - 1和y + 1兩條掃描線在區(qū)間[xLeft, xRight]中的像素,從xRight開(kāi)始向xLeft方向搜索,假設(shè)掃描的區(qū)間為AAABAAC(A為種子點(diǎn)顏色),那么將B和C前面的A作為種子點(diǎn)壓入棧中,然后返回第(2)步;
上述參考自參考文獻(xiàn)[4],做了些修改,文章[4]中描述算法,測(cè)試有一點(diǎn)問(wèn)題,所以做了修改.
可以看到該算法,基本上是一行一行著色的,這樣的話在大塊需要著色區(qū)域的效率比算法一要高很多。
ok,關(guān)于算法的步驟大家目前覺(jué)得模糊,一會(huì)可以參照我們的代碼。選定了算法以后,接下來(lái)就開(kāi)始編碼了。
3.編碼實(shí)現(xiàn)
我們代碼中引入了一個(gè)邊界顏色,如果設(shè)置的話,著色的邊界參考為該邊界顏色,否則會(huì)只要與種子顏色不一致為邊界。
(一)構(gòu)造方法與測(cè)量
public class ColourImageView extends ImageView
{
private Bitmap mBitmap;
/**
* 邊界的顏色
*/
private int mBorderColor = -1;
private boolean hasBorderColor = false;
private Stack<Point> mStacks = new Stack<Point>();
public ColourImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ColourImageView);
mBorderColor = ta.getColor(R.styleable.ColourImageView_border_color, -1);
hasBorderColor = (mBorderColor != -1);
L.e("hasBorderColor = " + hasBorderColor + " , mBorderColor = " + mBorderColor);
ta.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int viewWidth = getMeasuredWidth();
int viewHeight = getMeasuredHeight();
//以寬度為標(biāo)準(zhǔn),等比例縮放view的高度
setMeasuredDimension(viewWidth,
getDrawable().getIntrinsicHeight() * viewWidth / getDrawable().getIntrinsicWidth());
L.e("view's width = " + getMeasuredWidth() + " , view's height = " + getMeasuredHeight());
//根據(jù)drawable,去得到一個(gè)和view一樣大小的bitmap
BitmapDrawable drawable = (BitmapDrawable) getDrawable();
Bitmap bm = drawable.getBitmap();
mBitmap = Bitmap.createScaledBitmap(bm, getMeasuredWidth(), getMeasuredHeight(), false);
}
可以看到我們選擇的是繼承ImageView,這樣只需要將圖片設(shè)為src即可。
構(gòu)造方法中獲取我們的自定義邊界顏色,當(dāng)然可以不設(shè)置~~
重寫(xiě)測(cè)量的目的是為了獲取一個(gè)和View一樣大小的Bitmap便于我們操作。
接下來(lái)就是點(diǎn)擊啦~
4.onTouchEvent
@Override
public boolean onTouchEvent(MotionEvent event)
{
final int x = (int) event.getX();
final int y = (int) event.getY();
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
//填色
fillColorToSameArea(x, y);
}
return super.onTouchEvent(event);
}
/**
* 根據(jù)x,y獲得改點(diǎn)顏色,進(jìn)行填充
*
* @param x
* @param y
*/
private void fillColorToSameArea(int x, int y)
{
Bitmap bm = mBitmap;
int pixel = bm.getPixel(x, y);
if (pixel == Color.TRANSPARENT || (hasBorderColor && mBorderColor == pixel))
{
return;
}
int newColor = randomColor();
int w = bm.getWidth();
int h = bm.getHeight();
//拿到該bitmap的顏色數(shù)組
int[] pixels = new int[w * h];
bm.getPixels(pixels, 0, w, 0, 0, w, h);
//填色
fillColor(pixels, w, h, pixel, newColor, x, y);
//重新設(shè)置bitmap
bm.setPixels(pixels, 0, w, 0, 0, w, h);
setImageDrawable(new BitmapDrawable(bm));
}
可以看到,我們?cè)趏nTouchEvent中獲取(x,y),然后拿到改點(diǎn)坐標(biāo):
獲得點(diǎn)擊點(diǎn)顏色,獲得整個(gè)bitmap的像素?cái)?shù)組
改變這個(gè)數(shù)組中的顏色
然后重新設(shè)置給bitmap,重新設(shè)置給ImageView
重點(diǎn)就是通過(guò)fillColor去改變數(shù)組中的顏色
/**
* @param pixels 像素?cái)?shù)組
* @param w 寬度
* @param h 高度
* @param pixel 當(dāng)前點(diǎn)的顏色
* @param newColor 填充色
* @param i 橫坐標(biāo)
* @param j 縱坐標(biāo)
*/
private void fillColor(int[] pixels, int w, int h, int pixel, int newColor, int i, int j)
{
//步驟1:將種子點(diǎn)(x, y)入棧;
mStacks.push(new Point(i, j));
//步驟2:判斷棧是否為空,
// 如果棧為空則結(jié)束算法,否則取出棧頂元素作為當(dāng)前掃描線的種子點(diǎn)(x, y),
// y是當(dāng)前的掃描線;
while (!mStacks.isEmpty())
{
/**
* 步驟3:從種子點(diǎn)(x, y)出發(fā),沿當(dāng)前掃描線向左、右兩個(gè)方向填充,
* 直到邊界。分別標(biāo)記區(qū)段的左、右端點(diǎn)坐標(biāo)為xLeft和xRight;
*/
Point seed = mStacks.pop();
//L.e("seed = " + seed.x + " , seed = " + seed.y);
int count = fillLineLeft(pixels, pixel, w, h, newColor, seed.x, seed.y);
int left = seed.x - count + 1;
count = fillLineRight(pixels, pixel, w, h, newColor, seed.x + 1, seed.y);
int right = seed.x + count;
/**
* 步驟4:
* 分別檢查與當(dāng)前掃描線相鄰的y - 1和y + 1兩條掃描線在區(qū)間[xLeft, xRight]中的像素,
* 從xRight開(kāi)始向xLeft方向搜索,假設(shè)掃描的區(qū)間為AAABAAC(A為種子點(diǎn)顏色),
* 那么將B和C前面的A作為種子點(diǎn)壓入棧中,然后返回第(2)步;
*/
//從y-1找種子
if (seed.y - 1 >= 0)
findSeedInNewLine(pixels, pixel, w, h, seed.y - 1, left, right);
//從y+1找種子
if (seed.y + 1 < h)
findSeedInNewLine(pixels, pixel, w, h, seed.y + 1, left, right);
}
}
可以看到我已經(jīng)很清楚的將該算法的四個(gè)步驟標(biāo)識(shí)到該方法中。好了,最后就是一些依賴的細(xì)節(jié)上的方法:
/**
* 在新行找種子節(jié)點(diǎn)
*
* @param pixels
* @param pixel
* @param w
* @param h
* @param i
* @param left
* @param right
*/
private void findSeedInNewLine(int[] pixels, int pixel, int w, int h, int i, int left, int right)
{
/**
* 獲得該行的開(kāi)始索引
*/
int begin = i * w + left;
/**
* 獲得該行的結(jié)束索引
*/
int end = i * w + right;
boolean hasSeed = false;
int rx = -1, ry = -1;
ry = i;
/**
* 從end到begin,找到種子節(jié)點(diǎn)入棧(AAABAAAB,則B前的A為種子節(jié)點(diǎn))
*/
while (end >= begin)
{
if (pixels[end] == pixel)
{
if (!hasSeed)
{
rx = end % w;
mStacks.push(new Point(rx, ry));
hasSeed = true;
}
} else
{
hasSeed = false;
}
end--;
}
}
/**
* 往右填色,返回填充的個(gè)數(shù)
*
* @return
*/
private int fillLineRight(int[] pixels, int pixel, int w, int h, int newColor, int x, int y)
{
int count = 0;
while (x < w)
{
//拿到索引
int index = y * w + x;
if (needFillPixel(pixels, pixel, index))
{
pixels[index] = newColor;
count++;
x++;
} else
{
break;
}
}
return count;
}
/**
* 往左填色,返回填色的數(shù)量值
*
* @return
*/
private int fillLineLeft(int[] pixels, int pixel, int w, int h, int newColor, int x, int y)
{
int count = 0;
while (x >= 0)
{
//計(jì)算出索引
int index = y * w + x;
if (needFillPixel(pixels, pixel, index))
{
pixels[index] = newColor;
count++;
x--;
} else
{
break;
}
}
return count;
}
private boolean needFillPixel(int[] pixels, int pixel, int index)
{
if (hasBorderColor)
{
return pixels[index] != mBorderColor;
} else
{
return pixels[index] == pixel;
}
}
/**
* 返回一個(gè)隨機(jī)顏色
*
* @return
*/
private int randomColor()
{
Random random = new Random();
int color = Color.argb(255, random.nextInt(256), random.nextInt(256), random.nextInt(256));
return color;
}
ok,到此,代碼就介紹完畢了~~~
最后貼下布局文件~~
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:zhy="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<com.zhy.colour_app_01.ColourImageView
zhy:border_color="#FF000000"
android:src="@drawable/image_007"
android:background="#33ff0000"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:layout_height="match_parent"/>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ColourImageView">
<attr name="border_color" format="color|reference"></attr>
</declare-styleable>
</resources>
- Android仿開(kāi)心消消樂(lè)大樹(shù)星星無(wú)限循環(huán)效果
- Android游戲源碼分享之2048
- Unity3D游戲引擎實(shí)現(xiàn)在Android中打開(kāi)WebView的實(shí)例
- Android游戲開(kāi)發(fā)實(shí)踐之人物移動(dòng)地圖的平滑滾動(dòng)處理
- Android 游戲開(kāi)發(fā)之Canvas畫(huà)布的介紹及方法
- Android游戲開(kāi)發(fā)之碰撞檢測(cè)(矩形碰撞、圓形碰撞、像素碰撞)
- Android五子棋游戲程序完整實(shí)例分析
- Android高仿2048小游戲?qū)崿F(xiàn)代碼
- Android開(kāi)心消消樂(lè)代碼實(shí)例詳解
- Android 2d游戲開(kāi)發(fā)之貪吃蛇基于surfaceview
相關(guān)文章
Android編程簡(jiǎn)單實(shí)現(xiàn)ImageView點(diǎn)擊時(shí)背景圖修改的方法
這篇文章主要介紹了Android編程簡(jiǎn)單實(shí)現(xiàn)ImageView點(diǎn)擊時(shí)背景圖修改的方法,涉及Android針對(duì)背景圖相關(guān)屬性設(shè)置的操作技巧,需要的朋友可以參考下2015-12-12
android Launcher3設(shè)置默認(rèn)桌面應(yīng)用
這篇文章主要為大家詳細(xì)介紹了android Launcher3設(shè)置默認(rèn)桌面應(yīng)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07
Android根據(jù)電話號(hào)碼獲得聯(lián)系人頭像實(shí)例代碼
這篇文章主要介紹了Android根據(jù)電話號(hào)碼獲得聯(lián)系人頭像實(shí)例代碼,是Android程序開(kāi)發(fā)中非常重要的技巧,需要的朋友可以參考下2014-09-09
Android動(dòng)態(tài)加載布局實(shí)現(xiàn)技巧介紹
通過(guò)使用LayoutInflater 每次點(diǎn)擊按鈕時(shí)候去讀取布局文件,然后找到布局文件里面的各個(gè)VIEW 操作完VIEW 后加載進(jìn)我們setContentView 方面里面的要放的布局文件里面,每次動(dòng)態(tài)加載文件必需調(diào)用 removeAllViews方法,清除之前的加載進(jìn)來(lái)的View2022-12-12
Android短信操作常見(jiàn)協(xié)議和常用代碼
這篇文章主要介紹了Android短信操作常見(jiàn)協(xié)議和常用代碼,本文直接給出代碼實(shí)例,需要的朋友可以參考下2015-04-04
Android 驅(qū)動(dòng)編寫(xiě)LED-NDK程序
這篇文章主要介紹了Android 驅(qū)動(dòng)編寫(xiě)LED-NDK程序的相關(guān)資料,需要的朋友可以參考下2016-09-09
RecyclerView實(shí)現(xiàn)流式標(biāo)簽單選多選功能
RecyclerView是Android一個(gè)更強(qiáng)大的控件,其不僅可以實(shí)現(xiàn)和ListView同樣的效果,還有優(yōu)化了ListView中的各種不足。這篇文章主要介紹了RecyclerView實(shí)現(xiàn)的流式標(biāo)簽單選多選功能,需要的朋友可以參考下2019-11-11

