Android应用调用摄像头开发例子
这两天玩Android玩的废寝忘食,Blog都好几天没加东西了,惭愧!记录一下这两天最崩溃的一个问题。
好早就装了开发环境,真正着手还是这两天,非常的生疏,虽然有SDK文档,那么多蚊子一般的字,实在没心思慢慢研究。这不想调用摄像头,原以为很容易就能搞定的,累计花了大概有一天的时间才只能保证不出错……至于效果嘛,难说啊!
先看API-examples里有调用 摄像头的例子,在模拟器上虽然看不出什么效果,毕竟还是能执行的,就是一个方块在黑白相间的背景上移动呗。
就这么一个Google提供的范例,传到我的HTC G2上也能一执行就报错,我对Google的尊敬之情顿时减少了0.0001%啊……(当然有可能是G2不够标准,但毕竟其他的软件都是能用的,看来是有不少健壮代码了啊)。联机调试看了一下,出错的这一行(android-7里的):
parameters.setPreviewSize(w, h);
查一下,摄像头不是所有随便的(w, h)都能够认识的,所以呢,我们有了下面这样的增强版:
List<Size> mSupportedPreviewSizes;
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
后来的Sample里有了这段代码,看起来强大了不少。然而非常不幸的,首先getSupportedPreviewSizes()这个函数在2.1之后才有,我一开始是打算用1.6开发的……好吧我改,这个先不说,自己的手机已经刷到2.1了,这个函数的返回值居然是null?!如果确实想老版本上也用的话,怎么办??
有鉴于有软件可以达成,所以肯定是有方法的!得这么写:
public class SupportedSizesReflect {
private static Method Parameters_getSupportedPreviewSizes = null;
private static Method Parameters_getSupportedPictureSizes = null;
static {
initCompatibility();
};
private static void initCompatibility() {
try {
Parameters_getSupportedPreviewSizes = Camera.Parameters.class.getMethod(
"getSupportedPreviewSizes", new Class[] {});
Parameters_getSupportedPictureSizes = Camera.Parameters.class.getMethod(
"getSupportedPictureSizes", new Class[] {});
} catch (NoSuchMethodException nsme) {
nsme.printStackTrace();
Parameters_getSupportedPreviewSizes = Parameters_getSupportedPictureSizes = null;
}
}
/**
* Android 2.1之后有效
* @param p
* @return Android1.x返回null
*/
public static List<Size> getSupportedPreviewSizes(Camera.Parameters p) {
return getSupportedSizes(p, Parameters_getSupportedPreviewSizes);
}
public static List<Size> getSupportedPictureSizes(Camera.Parameters p){
return getSupportedSizes(p, Parameters_getSupportedPictureSizes);
}
@SuppressWarnings("unchecked")
private static List<Size> getSupportedSizes(Camera.Parameters p, Method method){
try {
if (method != null) {
return (List<Size>) method.invoke(p);
} else {
return null;
}
} catch (InvocationTargetException ite) {
Throwable cause = ite.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
throw new RuntimeException(ite);
}
} catch (IllegalAccessException ie) {
return null;
}
}
}
啊啊~,リフレクションなんか、大嫌い……然后还要用类似这样的方法调用~
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
Camera.Parameters params = camera.getParameters();
List<Size> supportedPictureSizes
= SupportedSizesReflect.getSupportedPictureSizes(params);
List<Size> supportedPreviewSizes
= SupportedSizesReflect.getSupportedPreviewSizes(params);
if ( supportedPictureSizes != null &&
supportedPreviewSizes != null &&
supportedPictureSizes.size() > 0 &&
supportedPreviewSizes.size() > 0) {
//2.x
pictureSize = supportedPictureSizes.get(0);
int maxSize = 1280;
if(maxSize > 0){
for(Size size : supportedPictureSizes){
if(maxSize >= Math.max(size.width,size.height)){
pictureSize = size;
break;
}
}
}
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
DisplayMetrics displayMetrics = new DisplayMetrics();
display.getMetrics(displayMetrics);
previewSize = getOptimalPreviewSize(
supportedPreviewSizes,
display.getWidth(),
display.getHeight());
params.setPictureSize(pictureSize.width, pictureSize.height);
params.setPreviewSize(previewSize.width, previewSize.height);
}
this.camera.setParameters(params);
try {
this.camera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
this.camera.startPreview();
}
死机无数次之后总结出来的啊,发现程序写的一个不好强制结束了,摄像头都无法再次启用了,kill都不行,只能重新启动手机才好。重启一次还那么慢,谁知道有比较适合G2的row?
哦还有一个,预览画面90°的,2.X后可以用parameters.set(“rotation”, “90″),之前的话得写成parameters.set(“orientation”, “portrait”)。但是据说不是所有的机器都可以的…
上面讲的是摄像头的初始化,如果觉得这么就万事OK的话,那就大错特错了。接下来的东西让人感到更加头痛。
在我的这个应用里,不需要把拍下来的图片存储,只需要把预览的图片数据处理一下就好,很自然的我只是用了onPreviewFrame调用,考虑处理传递进来的data数据流就是了。
网上很多帖子都说,然后用BitmapFactory的decodeByteArray()函数来解析图片就行了,我试了一下,发现这真是彻头彻尾的谎言,data字节流默认是YCbCr_420_SP(虽然可以改,但其他的格式未必兼容),decodeByteArray()压根儿不认!SDK2.2之后,似乎提供了一个YuvImage的类来转一下(那Google一开始提供这个借口是做什么的?),难道就要把老机给抛弃了么??万万不能啊(穷人最理解穷人们了)!
好在这个世界总是不缺少好人和牛人的,有人提供了这么一段转换的代码:
static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) {
final int frameSize = width * height;
for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0) y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);
if (r < 0) r = 0; else if (r > 262143) r = 262143;
if (g < 0) g = 0; else if (g > 262143) g = 262143;
if (b < 0) b = 0; else if (b > 262143) b = 262143;
rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
}
}
}
我不是很清楚这里面的原理,但是它能在我这里工作,暂时可以了……然后你才可以吧处理完的rgb[]传给decodeByteArray()。
顺便好心的把使用SDK2.2之后的也贴上吧,万一有用呢……
public void onPreviewFrame(byte[] data, Camera arg1) {
FileOutputStream outStream = null;
try {
YuvImage yuvimage = new YuvImage(data,ImageFormat.NV21,arg1.getParameters().getPreviewSize().width,arg1.getParameters().getPreviewSize().height,null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
yuvimage.compressToJpeg(new Rect(0,0,arg1.getParameters().getPreviewSize().width,arg1.getParameters().getPreviewSize().height), 80, baos);
outStream = new FileOutputStream(String.format("/sdcard/%d.jpg", System.currentTimeMillis()));
outStream.write(baos.toByteArray());
outStream.close();
Log.d(TAG, "onPreviewFrame - wrote bytes: " + data.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
Preview.this.invalidate();
}
哦,得到的图像旋转了90°(似乎有的机型设置一下setRotation(90)可以搞定,但还是那句话,不通用啊,况且这个是2.1之后的API)。手动转一下吧……
Matrix matrix = new Matrix();
matrix.postRotate(90);
// 这里的rgb就是刚刚转换处理的东东
Bitmap bmp = Bitmap.createBitmap(rgb, 0, w, w, h, Bitmap.Config.ARGB_4444);
Bitmap nbmp = Bitmap.createBitmap(bmp,
0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
终于正常了~~~
考虑到需要做识别,自然得先把它转成灰度图像,经典心理公式Gray = R*0.299 + G*0.587 + B*0.114出场了,但是手机的计算速度不那么快,这样的浮点运算还是尽量避免吧~ 于是考虑Gray = (R*299 + G*587 + B*114 + 500) / 1000或者Gray = (R*30 + G*59 + B*11 + 50) / 100。但是除法总是还是不够快,用移位吧……Gray = (R*19595 + G*38469 + B*7472) >> 16,稍微小一点,用Gray = (R*38 + G*75 + B*15) >> 7也足够了。
经过一番努力学习,把写就的代码兴致勃勃的在手机上跑了一下,虽然不够快结果出来了,想想也是大负荷运算啊,自我安慰客户应该可以有这样的耐心吧。
就在这个时候,我突然想起一件很重要的事情!
我需要的是灰度图,也就是亮度风量,而最开始的YUV,不就是亮度色度饱和度么?!那么Y分类不就是我需要的灰度值吗!!我在做什么,辛辛苦苦转成RGB,再转成亮度,吃饱了撑着不是。想到这里我立刻用头撞墙九九一百八十一次,一悼念我那白白死去的脑细胞的在天之灵。立刻重写,删除大量代码,快多了,效果也好~~ 鄙视一下两小时前的自己!
最近试着玩玩Android开发,做一个小玩意儿的时候,总感觉默认的按钮样式太糟糕,看到网上几幅IPhone截图,觉得按钮有点感觉,就想着抄一个过来……相当的没有技术含量,只不过记性不好,记录一下。
PhotoShop上的准备
用简单的方法,设置background图片。
先渐变填充圆角矩形(半径4左右),渐变的首尾颜色自己定好了,不过过渡位置我试了下,大约是上图1所示,在40%和60%增加两个色标,值为首尾之差的1/3和2/3。
做出立体效果和阴影,如2所示
做出边框线,如3所示
然后调两个亮一点和暗一点的(没用过IPhone不知道有焦点和按下时什么样子我这里就偷懒弄个明暗变化)按钮。
应用到按钮上
在drawable里定义一个式样文件
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/game_button_pressed" />
<item android:state_focused="true" android:drawable="@drawable/game_button_focused" />
<item android:drawable="@drawable/game_button_normal" />
</selector>
然后按钮的background指向它,这就完了~~
追记:
后来看到一种完全用XML定义按钮的方法,靠谱,虽然变化好像有些单调,但可以不用图片毕竟是好事,先记录一下方法,有空试试~
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<gradient android:startColor="#0d76e1" android:endColor="#0d76e1" android:angle="270" />
<stroke android:width="1dip" android:color="#f403c9" />
<corners android:radius="2dp" />
<padding android:left="10dp" android:top="10dp"
android:right="10dp" android:bottom="10dp" />
</shape>
</item>
<item android:state_focused="true">
<shape>
<gradient android:startColor="#ffc2b7" android:endColor="#ffc2b7" android:angle="270" />
<stroke android:width="1dip" android:color="#f403c9" />
<corners android:radius="2dp" />
<padding android:left="10dp" android:top="10dp"
android:right="10dp" android:bottom="10dp" />
</shape>
</item>
<item>
<shape>
<gradient android:startColor="#000000" android:endColor="#ffffff" android:angle="180" />
<stroke android:width="1dip" android:color="#f403c9" />
<corners android:radius="5dip" />
<padding android:left="10dp" android:top="10dp"
android:right="10dp" android:bottom="10dp" />
</shape>
</item>
</selector>
最近在一个Android应用中,用到了帧动画。这东西的具体介绍就不讲了,网上到处是(虽然基本都是抄来抄去……)。用起来很简单效果也很好,不过这一次我有一个特殊的要求,希望帧动画在播放完毕的时候做一些其他的事情。
在渐变动画中,我们可以很简单的使用监听器:
XXX.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
//渐变动画结束....
}
});
然而帧动画却没有这样方便的功能,为什么??
当然,我们自己写的动画,自己应该是知道它会什么时候结束,启动动画的时候顺便启动一个线程就好了,说说简单做起来毕竟也是要花时间的啊,咱自己写一个类吧,以后就好重复用了,共享出来,要是谁也有这样的要求就直接用吧!
public abstract class MyAnimationDrawable extends AnimationDrawable {
Handler finishHandler; // 判断结束的Handler
public MyAnimationDrawable(AnimationDrawable ad) {
// 这里得自己把每一帧加进去
for (int i = 0; i < ad.getNumberOfFrames(); i++) {
this.addFrame(ad.getFrame(i), ad.getDuration(i));
}
}
@Override
public void start() {
super.start();
/**
* 首先用父类的start()
* 然后启动线程,来调用onAnimationEnd()
*/
finishHandler = new Handler();
finishHandler.postDelayed(
new Runnable() {
public void run() {
onAnimationEnd();
}
}, getTotalDuration());
}
/**
* 这个方法获得动画的持续时间(之后调用onAnimationEnd())
*/
public int getTotalDuration() {
int durationTime = 0;
for (int i = 0; i < this.getNumberOfFrames(); i++) {
durationTime += this.getDuration(i);
}
return durationTime;
}
/**
* 结束时调用的方法,一定要实现
*/
abstract void onAnimationEnd();
}
/******************************************
***************** 使用方法 *****************
******************************************/
// 新建我们的类的实例
MyAnimationDrawable mad = new MyAnimationDrawable(
(AnimationDrawable) getResources().getDrawable(R.drawable.anim1)) {
@Override
void onAnimationEnd() {
// 实现这个方法,结束后会调用
}
};
// 把这个动画“赐福”给某个ImageView
iv.setBackgroundDrawable(mad);
// 开始吧
mad.start();
但是这个方法未必好,为什么呢,因为我们没法保证动画会完整流畅的播放完毕,也许因为其他的事情终止了,也不知到机器卡的时候会不会使动画播放时间变长,仅用在要求不高的情况下,至少,我这里是够了。要是有更好方法一定告知~
自从Android 2.1中加入了动态壁纸,一下子牛叉了很多啊,漂亮的壁纸层出不穷,看上去老土的Android手机总算也是可以炫一下了。
动态壁纸基础
制作方法,网上太多了,虽然基本都是抄的,其实都是从sample上发展出来的,我也把要点记一下,每次写新的都要把老的工程打开看,啥记性……
res/xml中指定动态壁纸的xml文件:
<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="xxx.yyy.LiveWallpaperSettings"
android:thumbnail="@drawable/icon" />
这里是说明自己图标和设定Activity。
还有一个设定Activity的xml文件,就是普通的PreferenceScreen,省略。
创建WallpaperService的子类,需要再onCreateEngine方法中返回一个Engine,Engine中画画儿,就像用SurfaceView一样。
public class LiveWallpaper extends WallpaperService {
public static final String SHARED_PREFS_NAME = "setting_file_name";
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public Engine onCreateEngine() {
return new TestPatternEngine();
}
class TestPatternEngine extends Engine implements
SharedPreferences.OnSharedPreferenceChangeListener {
private final Handler mHandler = new Handler();
private final Runnable mDrawPattern = new Runnable() {
public void run() {
drawFrame();
}
};
private boolean mVisible;
private SharedPreferences mPreferences;
TestPatternEngine() {
mPreferences = LiveWallpaper.this.getSharedPreferences(
SHARED_PREFS_NAME, 0);
mPreferences.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(mPreferences, null);
}
public void onSharedPreferenceChanged(SharedPreferences prefs,
String key) {
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
}
@Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawPattern);
}
@Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
drawFrame();
} else {
mHandler.removeCallbacks(mDrawPattern);
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
drawFrame();
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
mHandler.removeCallbacks(mDrawPattern);
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset, float xStep,
float yStep, int xPixels, int yPixels) {
drawFrame();
}
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// draw something
}
} finally {
if (c != null)
holder.unlockCanvasAndPost(c);
}
mHandler.removeCallbacks(mDrawPattern);
if (mVisible) {
mHandler.postDelayed(mDrawPattern, 1000 / 25);
}
}
}
}
还有设定Activity的实现:
public class LiveWallpaperSettings extends PreferenceActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
getPreferenceManager().setSharedPreferencesName(LiveWallpaper.SHARED_PREFS_NAME);
addPreferencesFromResource(R.xml.livewallpaper_settings);
getPreferenceManager().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onDestroy() {
getPreferenceManager().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
super.onDestroy();
}
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
}
}
androidmanifest.xml中一定要加入的:
<service android:name=".LiveWallpaper" android:label="@string/app_name" android:icon="@drawable/icon">
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>
<meta-data android:name="android.service.wallpaper" android:resource="@xml/livewallpaper" />
</service>
<activity android:label="@string/livewallpaper_settings"
android:name=".LiveWallpaperSettings"
android:theme="@android:style/Theme.Light.WallpaperSettings"
android:exported="true"
android:icon="@drawable/icon">
</activity>
<uses-sdk android:minSdkVersion="7" />
<uses-feature android:name="android.software.live_wallpaper" />
“uses-feature android:name=”android.software.live_wallpaper””这句话要慎用啊!Google的电子市场会认这句话,然后把有些可以用的机器过滤掉,比如俺的破机器,本来是不支持的,升级到2.1按说是可以用的。我看了市场上很多有名的动态壁纸,就没有加这句话~~
另外,如果想在动态壁纸中使用OpenGL ES,请参考这篇文章:Android中使用OpenGL ES的一二事。
加入AdMob广告
上面都是废话,我这次想说的主题是如何在壁纸设定界面里加入AdMob广告,好不容易做好的东西,总是要意思意思是吧,加个广告是没法的事情了。
不同于一般的Activity,直接加入AdMob是不行的,连个Layout都没有,而且直接加到壁纸上则非常糟糕,谁也不希望好好的背景上塞一个广告。所以一般动态壁纸的广告都加在设置界面里,这样还真有些不容易。
一般有两种方法,把Admob的adView当做一个Preference,直接加入xml就好。
public class AdmobPreference extends Preference {
public AdmobPreference(Context context) {
super(context, null);
}
public AdmobPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected View onCreateView(ViewGroup parent) {
//override here to return the admob ad instead of a regular preference display
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return inflater.inflate(R.layout.admob_preference, null);
}
}
相对的admob_preference的配置:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ad_layout"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<com.google.ads.AdView
xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads"
android:id="@+id/ad"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
ads.adSize="BANNER"
ads:adUnitId="a14e51ca560d266"
ads:loadAdOnCreate="true"
/>
</LinearLayout>
Android AdView missing required XML attribute "adsize"
这里还是有些学问的,网上查一下会有无数的“AdView missing required XML attribute ‘adSize’”的问题,据说是4.1.0之后带来的问题,一定要这么写(将”http://schemas.android.com/apk/lib/com.google.ads”当做一个命名空间,而不是加入自己的程序中)才能不出错,至少大家是如此说的,可惜我不行,怎么写都有这个错,耗费了两个小时尝试了各种写法,还是不行啊!!最后还是写在代码里了:(
不过光这么写貌似还是不行,AdView周围还有留白,貌似是PreferenceActivity里的padding,去不掉,最后还是使用了Tab的方式:
public class SettingsTabActivity extends TabActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.table_layout);
TabHost tabHost = getTabHost(); // The activity TabHost
TabHost.TabSpec spec; // Resusable TabSpec for each tab
Intent intent; // Reusable Intent for each tab
// Create an Intent for the regular live wallpaper preferences activity
intent = new Intent().setClass(this, LivePaperSettings.class);
// Initialize a TabSpec and set the intent
spec = tabHost.newTabSpec("TabTitle").setContent(intent);
spec.setIndicator("TabTitle");
tabHost.addTab(spec);
tabHost.setCurrentTab(0);
}
对应的layout:
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads"
android:id="@android:id/tabhost" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:orientation="vertical"
android:id="@+id/main_layout"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<com.google.ads.AdView android:id="@+id/ad"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
ads:backgroundColor="#000000"
ads:primaryTextColor="#FFFFFF"
ads:secondaryTextColor="#CCCCCC"
ads.adSize="BANNER"
ads:adUnitId="a14e51ca560d266"
ads:loadAdOnCreate="true" />
<TabWidget android:id="@android:id/tabs"
android:layout_width="fill_parent" android:layout_height="1dp"
android:visibility="invisible" />
<FrameLayout android:id="@android:id/tabcontent"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:padding="1dp" />
</LinearLayout>
</TabHost>
大概就是如此了。
对了还有一个浪费了我两个多小时的问题,对于已经按照的live wallpaper,你修改了setting的Activity,直接覆盖安装是没效果的,点配置还是出现以前的Activity,发现这一点之前,我做了不知几百次修改上传,都快整挂了,Google怎么考虑的?!
单选按钮就是一组按钮只能选中其中的一个了,这个在html中有一个单选按钮组,那么在android开发中要如何来实现,具体的我们来看看。我们经常在淘宝下单时,会选择尺寸,样式,在一排按钮里设置只能选中一个,我们知道按钮Button是不能唯一选中的,当我们点击一排按钮时我们会发现这一排全会被选择。下面我们就来看一下怎么样实现只选中一个按钮。
首先根据获取的数据动态生成一排按钮。
List<SizeEntity> sizeEntityList=productEntity.getSize();
if(sizeEntityList!=null&&sizeEntityList.size()>0) {
for (int i = 0; i < sizeEntityList.size(); i++) {
final SizeEntity size=sizeEntityList.get(i);
final Button button = new Button(getContext());
button.setId(i);
button.setText(sizeEntityList.get(i).getName());
button.setBackgroundResource(R.drawable.shape_round_corner_0_stroke_bg_grey);
button.setTextSize(10);
mSizeContent.addView(button, Math.min(1, mSizeContent.getChildCount()));
}
}
下面来处理点击事件:
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
for (int j = 0; j < mSizeContent.getChildCount(); j++) {
final Button mbutton = (Button) mSizeContent.getChildAt(j);
if (button == mbutton) {
mbutton.setBackgroundResource(R.color.red);
mbutton.setTextColor(getResources().getColor(R.color.white));
} else {
mbutton.setBackgroundResource(R.drawable.shape_round_corner_0_stroke_bg_grey);
mbutton.setTextColor(getResources().getColor(R.color.black));
}
}
}
});
一个raid例子
xml文件
<LinearLayout 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"
tools:context=".MainActivity"
android:orientation="vertical">
<TextView
android:id="@+id/txt"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="您的性别为"/>
<RadioGroup
android:id="@+id/sex"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/male"
android:text="男"/>
<RadioButton
android:id="@+id/female"
android:text="女"/>
</RadioGroup>
</LinearLayout>
java文件
public class MainActivity extends Activity {
private TextView txt=null;
private RadioGroup sex=null;
private RadioButton male=null;
private RadioButton female=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.txt=(TextView) super.findViewById(R.id.txt);
this.sex=(RadioGroup) super.findViewById(R.id.sex);
this.male=(RadioButton) super.findViewById(R.id.male);
this.female=(RadioButton) super.findViewById(R.id.female);
this.sex.setOnCheckedChangeListener(new OnCheckedChangeListenerImp());
}
private class OnCheckedChangeListenerImp implements OnCheckedChangeListener{
public void onCheckedChanged(RadioGroup group, int checkedId) {
String temp=null;
if(MainActivity.this.male.getId()==checkedId){
temp="男";
}
else if(MainActivity.this.female.getId()==checkedId){
temp="女";
}
MainActivity.this.txt.setText("您的性别是"+temp);
}
}
}
相关文章
- 这篇文章主要介绍了Spring AOP 对象内部方法间的嵌套调用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-08-29
- 下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
- <?php require('path.inc.php'); header('content-Type: text/html; charset=utf-8'); $borough_id = intval($_GET['id']); if(!$borough_id){ echo ' ...2016-11-25
- 这篇文章主要介绍了c# 三种方法调用WebService接口的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-07
Android开发中findViewById()函数用法与简化
findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20- 如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
- 夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
- 为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
- 如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
- 这篇文章主要介绍了js实现调用网络摄像头及常见错误处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-07
- 深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
- 这篇文章主要介绍了解决Vue watch里调用方法的坑,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-11-07
- 下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
- java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
- TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
- 这篇文章介绍了c#动态调用Webservice的两种方法实例,有需要的朋友可以参考一下...2020-06-25
- 这篇文章主要给大家介绍了关于c#中的WebService及其调用方式的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
- 这篇文章主要介绍了解决vue watch数据的方法被调用了两次的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-11-07
android.os.BinderProxy cannot be cast to com解决办法
本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20- 这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15