Android子控件超出父控件的范围显示出来方法
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="60dp"
android:background="@mipmap/www" />
<LinearLayout
android:id="@+id/ll_bottom"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:background="#F8549D"
android:elevation="10dp"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:layout_weight="1">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@mipmap/ic_launcher" />
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:layout_weight="1">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@mipmap/ic_launcher" />
</RelativeLayout>
<RelativeLayout
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_gravity="bottom"
android:background="@drawable/bottom_bg_shape">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerInParent="true"
android:src="@mipmap/icon_go" />
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:layout_weight="1">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@mipmap/ic_launcher" />
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:layout_weight="1">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@mipmap/ic_launcher" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
这张图呢是我刚刚做的,效果是参考了酷狗音乐播放器来完成的,我想说明重点在底部的导航栏:如图
android:clipChildren="false"
属性解释和说明:
1、android:clipChildren的意思:是否限制子View在其范围内
2、可以通过android:layout_gravity控制超出的部分如何显示。
3、只需在根节点设置android:clipChildren为false即可,默认为true
下面我们来看一篇android Html.fromHtml使用方法,希望文章能够让各位深入的理解到android Html.fromHtml使用细节哦。项目中往往需要显示一段文本,如果对文本需要特定的效果,就要写自定义的span,这样的工作量会变得很大,目前android支持html格式的文本。提供了两个接口,下面我们就来看一下怎么使用。
1. Spanned android.text.Html.fromHtml(String source) //输入的参数为(html格式的文本)
目前android不支持全部的html的标签,目前只支持与文本显示和段落等标签,对于图片和其他的多媒体,还有一些自定义标签不能识别;
例子:
TextView t3 = (TextView) findViewById(R.id.text3);
t3.setText(Html.fromHtml( "<b>text3:</b> Text with a " + "<a href=\"http://www.google.com\">link</a> " +"created in the Java source code using HTML."));
另外也可以在string.xml中使用,但是要用用<!–cdata–>去转义。如下例子:
<string name="htmlFormattedText">
<![CDATA[
Text with markup for [b]bold[/b]
and [i]italic[/i] text.
There is also support for a
<tt>teletype-style</tt> font.
But no use for the <code>code</code>
tag!
]]></string>
TextView view = (TextView)findViewById(R.id.sampleText);
String formattedText = getString(R.string.htmlFormattedText);
Spanned result = Html.fromHtml(formattedText);
view.setText(result);
2.Spanned android.text.Html.fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler)
Source: 需处理的html文本
imageGetter :对图片处理(处理html中的图片标签)
tagHandler :对标签进行处理(相当于自定义的标签处理,在这里面可以处理自定义的标签)
具体不细说,大家感兴趣可以自行学习;在实际项目里我们就可以使用第一种方式来替代之前我们用的自定义span了。
下面小编为各位整理一些关于安卓开发ViewPager图片预览之图片的放大缩小,移动,切换的基本知识与例子,具体的如下介绍。1,自由的放大和缩小
2.双击放大与缩小
3.放大以后可以进行自由的移动
4.处理与ViewPager之间的的事件冲突
需要用到的知识点
1.Matrix (图片放大,缩小需要用到矩阵)
2.ScaleGestureDetector(检测用户多指触控时缩放的手势)
3.GestureDetector:检测用户双击时需要做的一些处理
4.事件分发机制(当我们图片放大时,我们的图片是可以左右移动的,在ViewPager左右切换图片,两者会有冲突)。
----------------------------------------------------代码设计
第一课
第一步 :自定义ImageView 实现图片自适应控件大小:(效果是:图片小于控件大小时,放大到控件大小,图片大于控件大小时,自动缩小到控件大小)
package com.example.viewpagerimage;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;
//实现监听器OnGlobalLayoutListener,监听图片是否加载完成
public class MyImageView extends ImageView implements OnGlobalLayoutListener{
private boolean mOnce;//判断是否初始化
private float mInitScale;//初始化时缩放的值
private float mMidScale;//双击放大到达的值
private float mMaxScale;//放大的最大值
private Matrix mScaleMatrix;
public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//init
mScaleMatrix = new Matrix();
setScaleType(ScaleType.MATRIX);
//当图片加载时,图片可能很大,也可能很小,需要让图片自适应屏幕大小,当图片太大时自动缩小到屏幕大小,当图片太小时放大到屏幕大小。
}
public MyImageView(Context context, AttributeSet attrs) {
this(context, attrs,0);
// TODO Auto-generated constructor stub
}
public MyImageView(Context context) {
this(context,null);
// TODO Auto-generated constructor stub
}
@Override
protected void onAttachedToWindow() {
// TODO Auto-generated method stub
super.onAttachedToWindow();//当View 显示在屏幕上时调用
getViewTreeObserver().addOnGlobalLayoutListener(this);//注册接口
}
@SuppressWarnings("deprecation")
@Override
protected void onDetachedFromWindow() {
// TODO Auto-generated method stub
super.onDetachedFromWindow();//当View从屏幕上移除时调用
getViewTreeObserver().removeGlobalOnLayoutListener(this);//移除接口
}
/**
* 获取ImageView加载完成的图片
*/
@Override
public void onGlobalLayout() {
// 全局的布局完成后调用
if(!mOnce){
//得到控件的宽和高
int width = getWidth();
int height = getHeight();
//得到我们的图片以及宽和高
Drawable d = getDrawable();
if(d == null)
return;
int dw = d.getIntrinsicWidth();
int dh = d.getIntrinsicHeight();
float scale = 1.0f;//缩放值
//如果图片的宽度大于控件高度,但是宽度小于控件的宽度,将其缩小
if(dw > width && dh < height){
scale = width*1.0f/dw;
}
else if(dh > height && dw < width){
scale = height*1.0f /dh;
}
else if(dw > width && dh > height){
scale = Math.min(width*1.0f/dw, height*1.0f/dh);
}
else if(dw < width && dh < height){
scale = Math.min(width *1.0f/dw, height*1.0f/dh);
}
/*
* 得到初始化时缩放的比例
* */
mInitScale = scale;
mMaxScale = mInitScale * 4;
mMidScale = mInitScale * 2;
//将图片移动到当前控件的中心
int dx = getWidth()/2 - dw /2;
int dy = getHeight()/2 - dh/2;
mScaleMatrix.postTranslate(dx, dy);//平移
mScaleMatrix.postScale(mInitScale, mInitScale,width/2,height/2);//缩放,后面两个参数是缩放的中心点
setImageMatrix(mScaleMatrix);
mOnce = true;
}
}
}
布局文件使用:
<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="com.example.viewpagerimage.MainActivity" >
<com.example.viewpagerimage.MyImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix"
android:src="@drawable/viewpatherimage" />
</LinearLayout>
第二步:给自定义控件添加支持手指触控缩放的功能:(支持手指触控放大)
因为涉及到手势触摸事件所以要实现OnScaleGestureListener,OnTouchListener这两个接口。
声明成员变量: private ScaleGestureDetector mScaleGestureDetector;//捕获用户多指触控缩放的比例
在构造函数中初始化:
mScaleGestureDetector = new ScaleGestureDetector(context, this);
setOnTouchListener(this);
添加方法:
/**\
* 获取当前图片的缩放值
* @return
*/
public float getScale(){
float[] values = new float[9];
mScaleMatrix.getValues(values);
return values[Matrix.MSCALE_X];
}
实现接口中的方法:
//缩放的区间,initScale maxScale
@Override
public boolean onScale(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
float scale = getScale();
float scaleFactor = detector.getScaleFactor();//得到缩放的值
if(getDrawable() == null){
return true;
}
//缩放范围的控制
if((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f)){
if(scale * scaleFactor < mInitScale){
scaleFactor = mInitScale / scale;//当手指缩放小于最小值时 ,默认显示最小的比例
}
if(scale * scaleFactor > mMaxScale){//当手指缩放大于于最大值时 ,默认显示最大的比例
scale = mMaxScale/scale;
}
//缩放
mScaleMatrix.postScale(scaleFactor, scaleFactor, getWidth()/2, getHeight()/2);
setImageMatrix(mScaleMatrix);
}
return true;//设置完成返回true保证事件能够进行
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
return true;//必须返回true
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
}
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
mScaleGestureDetector.onTouchEvent(event);//把event传递给mscaleGestureDetector处理
return true;//必须返true
}
全部代码如下:
package com.example.viewpagerimage;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;
import android.view.View.OnTouchListener;
//实现监听器OnGlobalLayoutListener,监听图片是否加载完成
public class MyImageView extends ImageView implements OnGlobalLayoutListener, OnScaleGestureListener,OnTouchListener{
private boolean mOnce;//判断是否初始化
private float mInitScale;//初始化时缩放的值
private float mMidScale;//双击放大到达的值
private float mMaxScale;//放大的最大值
private ScaleGestureDetector mScaleGestureDetector;//捕获用户多指触控缩放的比例
private Matrix mScaleMatrix;
public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//init
mScaleMatrix = new Matrix();
setScaleType(ScaleType.MATRIX);
mScaleGestureDetector = new ScaleGestureDetector(context, this);
setOnTouchListener(this);
//当图片加载时,图片可能很大,也可能很小,需要让图片自适应屏幕大小,当图片太大时自动缩小到屏幕大小,当图片太小时放大到屏幕大小。
}
public MyImageView(Context context, AttributeSet attrs) {
this(context, attrs,0);
// TODO Auto-generated constructor stub
}
public MyImageView(Context context) {
this(context,null);
// TODO Auto-generated constructor stub
}
@Override
protected void onAttachedToWindow() {
// TODO Auto-generated method stub
super.onAttachedToWindow();//当View 显示在屏幕上时调用
getViewTreeObserver().addOnGlobalLayoutListener(this);//注册接口
}
@SuppressWarnings("deprecation")
@Override
protected void onDetachedFromWindow() {
// TODO Auto-generated method stub
super.onDetachedFromWindow();//当View从屏幕上移除时调用
getViewTreeObserver().removeGlobalOnLayoutListener(this);//移除接口
}
/**
* 获取ImageView加载完成的图片
*/
@Override
public void onGlobalLayout() {
// 全局的布局完成后调用
if(!mOnce){
//得到控件的宽和高
int width = getWidth();
int height = getHeight();
//得到我们的图片以及宽和高
Drawable d = getDrawable();
if(d == null)
return;
int dw = d.getIntrinsicWidth();
int dh = d.getIntrinsicHeight();
float scale = 1.0f;//缩放值
//如果图片的宽度大于控件高度,但是宽度小于控件的宽度,将其缩小
if(dw > width && dh < height){
scale = width*1.0f/dw;
}
else if(dh > height && dw < width){
scale = height*1.0f /dh;
}
else if(dw > width && dh > height){
scale = Math.min(width*1.0f/dw, height*1.0f/dh);
}
else if(dw < width && dh < height){
scale = Math.min(width *1.0f/dw, height*1.0f/dh);
}
/*
* 得到初始化时缩放的比例
* */
mInitScale = scale;
mMaxScale = mInitScale * 4;
mMidScale = mInitScale * 2;
//将图片移动到当前控件的中心
int dx = getWidth()/2 - dw /2;
int dy = getHeight()/2 - dh/2;
mScaleMatrix.postTranslate(dx, dy);//平移
mScaleMatrix.postScale(mInitScale, mInitScale,width/2,height/2);//缩放,后面两个参数是缩放的中心点
setImageMatrix(mScaleMatrix);
mOnce = true;
}
}
/**\
* 获取当前图片的缩放值
* @return
*/
public float getScale(){
float[] values = new float[9];
mScaleMatrix.getValues(values);
return values[Matrix.MSCALE_X];
}
//缩放的区间,initScale maxScale
@Override
public boolean onScale(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
float scale = getScale();
float scaleFactor = detector.getScaleFactor();//得到缩放的值
if(getDrawable() == null){
return true;
}
//缩放范围的控制
if((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f)){
if(scale * scaleFactor < mInitScale){
scaleFactor = mInitScale / scale;//当手指缩放小于最小值时 ,默认显示最小的比例
}
if(scale * scaleFactor > mMaxScale){//当手指缩放大于于最大值时 ,默认显示最大的比例
scale = mMaxScale/scale;
}
//缩放
mScaleMatrix.postScale(scaleFactor, scaleFactor, getWidth()/2, getHeight()/2);
setImageMatrix(mScaleMatrix);
}
return true;//设置完成返回true保证事件能够进行
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
return true;//必须返回true
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
}
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
mScaleGestureDetector.onTouchEvent(event);//把event传递给mscaleGestureDetector处理
return true;//必须返true
}
}
目前实现的效果:无论手指触摸哪里都是以中心点位中心开始缩放的。
下面实现的效果是:以手指触控的任意点为中心开始缩放
第三课(第三步):支持以手指触控的任意点为中心开始缩放
关键部分是在缩放的时候不断进行边界检测,防止放大后缩小后出现白边:
/**
* 在缩放的时候进行边界控制范围位置控制
*/
private void checkBorderAndCenterWhenScale() {
// TODO Auto-generated method stub
RectF rect = getMatrixRectF();
float deltaX = 0;
float deltaY = 0;
float width = getWidth();
float height = getHeight();
//缩放时进行边界检测,放在出现白边
if(rect.width() >= width){
if(rect.left > 0){//处理左边的空白
deltaX = -rect.left;
}
if(rect.right < width){//处理右边的空白
deltaX = (int) (width - rect.right);
}
}
if(rect.height() >= height){
if(rect.top > 0){
deltaY = -rect.top;
}
if(rect.bottom < height){
deltaY = height - rect.bottom;
}
}
//如果宽度或高度小于控件的宽或高,则让其居中
if(rect.width() < width){
deltaX = width/2f -rect.right + rect.width()/2f;
}
if(rect.height() < height){
deltaY = height /2f -rect.bottom + rect.height()/2f;
}
mScaleMatrix.postTranslate(deltaX, deltaY);
}
全部代码:
package com.example.viewpagerimage;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;
//实现监听器OnGlobalLayoutListener,监听图片是否加载完成
public class MyImageView extends ImageView implements OnGlobalLayoutListener, OnScaleGestureListener,OnTouchListener{
private boolean mOnce;//判断是否初始化
private float mInitScale;//初始化时缩放的值
private float mMidScale;//双击放大到达的值
private float mMaxScale;//放大的最大值
private ScaleGestureDetector mScaleGestureDetector;//捕获用户多指触控缩放的比例
private Matrix mScaleMatrix;
public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//init
mScaleMatrix = new Matrix();
setScaleType(ScaleType.MATRIX);
mScaleGestureDetector = new ScaleGestureDetector(context, this);
setOnTouchListener(this);
//当图片加载时,图片可能很大,也可能很小,需要让图片自适应屏幕大小,当图片太大时自动缩小到屏幕大小,当图片太小时放大到屏幕大小。
}
public MyImageView(Context context, AttributeSet attrs) {
this(context, attrs,0);
// TODO Auto-generated constructor stub
}
public MyImageView(Context context) {
this(context,null);
// TODO Auto-generated constructor stub
}
@Override
protected void onAttachedToWindow() {
// TODO Auto-generated method stub
super.onAttachedToWindow();//当View 显示在屏幕上时调用
getViewTreeObserver().addOnGlobalLayoutListener(this);//注册接口
}
@SuppressWarnings("deprecation")
@Override
protected void onDetachedFromWindow() {
// TODO Auto-generated method stub
super.onDetachedFromWindow();//当View从屏幕上移除时调用
getViewTreeObserver().removeGlobalOnLayoutListener(this);//移除接口
}
/**
* 获取ImageView加载完成的图片
*/
@Override
public void onGlobalLayout() {
// 全局的布局完成后调用
if(!mOnce){
//得到控件的宽和高
int width = getWidth();
int height = getHeight();
//得到我们的图片以及宽和高
Drawable d = getDrawable();
if(d == null)
return;
int dw = d.getIntrinsicWidth();
int dh = d.getIntrinsicHeight();
float scale = 1.0f;//缩放值
//如果图片的宽度大于控件高度,但是宽度小于控件的宽度,将其缩小
if(dw > width && dh < height){
scale = width*1.0f/dw;
}
else if(dh > height && dw < width){
scale = height*1.0f /dh;
}
else if(dw > width && dh > height){
scale = Math.min(width*1.0f/dw, height*1.0f/dh);
}
else if(dw < width && dh < height){
scale = Math.min(width *1.0f/dw, height*1.0f/dh);
}
/*
* 得到初始化时缩放的比例
* */
mInitScale = scale;
mMaxScale = mInitScale * 4;
mMidScale = mInitScale * 2;
//将图片移动到当前控件的中心
int dx = getWidth()/2 - dw /2;
int dy = getHeight()/2 - dh/2;
mScaleMatrix.postTranslate(dx, dy);//平移
mScaleMatrix.postScale(mInitScale, mInitScale,width/2,height/2);//缩放,后面两个参数是缩放的中心点
setImageMatrix(mScaleMatrix);
mOnce = true;
}
}
/**
* 获取当前图片的缩放值
* @return
*/
public float getScale(){
float[] values = new float[9];
mScaleMatrix.getValues(values);
return values[Matrix.MSCALE_X];
}
//缩放的区间,initScale maxScale
@Override
public boolean onScale(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
float scale = getScale();
float scaleFactor = detector.getScaleFactor();//得到缩放的值
if(getDrawable() == null){
return true;
}
//缩放范围的控制
if((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f)){
if(scale * scaleFactor < mInitScale){
scaleFactor = mInitScale / scale;//当手指缩放小于最小值时 ,默认显示最小的比例
}
if(scale * scaleFactor > mMaxScale){//当手指缩放大于于最大值时 ,默认显示最大的比例
scale = mMaxScale/scale;
}
//缩放,缩放中心是手指触控的地方
mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(),detector.getFocusY());
checkBorderAndCenterWhenScale();
setImageMatrix(mScaleMatrix);
}
return true;//设置完成返回true保证事件能够进行
}
/**
* 获得图片放大缩小以后的宽和高以及l r t b
* @return
*/
private RectF getMatrixRectF(){
Matrix matrix = mScaleMatrix;
RectF recF = new RectF();
Drawable d = getDrawable();
if(d != null){
recF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
matrix.mapRect(recF);
}
return recF;
}
/**
* 在缩放的时候进行边界控制范围位置控制
*/
private void checkBorderAndCenterWhenScale() {
// TODO Auto-generated method stub
RectF rect = getMatrixRectF();
float deltaX = 0;
float deltaY = 0;
float width = getWidth();
float height = getHeight();
//缩放时进行边界检测,放在出现白边
if(rect.width() >= width){
if(rect.left > 0){//处理左边的空白
deltaX = -rect.left;
}
if(rect.right < width){//处理右边的空白
deltaX = (int) (width - rect.right);
}
}
if(rect.height() >= height){
if(rect.top > 0){
deltaY = -rect.top;
}
if(rect.bottom < height){
deltaY = height - rect.bottom;
}
}
//如果宽度或高度小于控件的宽或高,则让其居中
if(rect.width() < width){
deltaX = width/2f -rect.right + rect.width()/2f;
}
if(rect.height() < height){
deltaY = height /2f -rect.bottom + rect.height()/2f;
}
mScaleMatrix.postTranslate(deltaX, deltaY);
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
return true;//必须返回true
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
}
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
mScaleGestureDetector.onTouchEvent(event);//把event传递给mscaleGestureDetector处理
return true;//必须返true
}
}
前言
讲真,好久没写博客了,2016都过了一半了,赶紧重新捡起来。(个人感慨,和内容无关……
所谓RTL,顾名思义也就是Right To Left,是一种阿拉伯语、波斯语等情况下从右往左的阅读方式。当所开发的App等面向海外用户时需要做这个适配。
从Android 4.2开始支持原生的RTL模式,对此之前的版本我也不想多做说明,这些老版本要是还支持那Android碎片化就没完没了了。
正文
如何查看效果
首先要说的是,想要看RTL模式,不必去把手机中的语言/国家设置到阿拉伯等,只需要在“开发者选项”中勾选“强制使用从右到左的布局方向“,这样真的是方便太多了。
此处以MIUI为例,大家也不妨自己动手试试。
如图,原本左右两侧的控件发生了对调,值得注意的是图中红色方框标注的图标发生了翻转(更标准的说法是”镜像“)。
如何支持RTL
是不是很有意思呢,那么如果在你的App中适配RTL呢?
1,需要在清单文件总队RTL的支持做一个声明,放到< application >节点下。
android:supportsRtl="true"
2,将布局中的”left、right“相关的属性换成对应的”start、end“属性。
这一步可能用说的不够清晰,看代码看图!
<Button
android:id="@+id/button"
android:text="A"
android:layout_width="60dp"
android:layout_height="40dp" />
<Button
android:id="@+id/button2"
android:text="B"
android:layout_toRightOf="@id/button"
android:layout_width="60dp"
android:layout_height="40dp" />
<Button
android:id="@+id/button3"
android:text="C"
android:layout_toRightOf="@id/button2"
android:layout_width="60dp"
android:layout_height="40dp" />
应该可以看出来这是在一个相对布局中,默认情况下是这样的:
开启RTL后,却是这样的:
为什么B、C按钮不见了?因为根据属性,它们都在A的右边,这已经超出的屏幕边界。
如果我们对布局做一点修改:
android:layout_toRightOf
改成
android:layout_toEndOf
如果有left,也照搬改成start就好。
页面不想支持RTL怎么办
有一些界面你不想它支持RTL,或者它本身不需要支持,那又该如何呢?比如说拨号界面,难道要把数字键也镜像过去吗:
只需要加上这么一句就好了呀。
layoutDirect可以使用4种属性:
ltr:从左往右
rtl:从右往左
inherit:从上层视图中继承
locale:由Locale决定
分别对应的int值为0,1,2,3。
图片怎么办
只需要创建一个文件夹,把镜像后的图片放进去即可,代码中不用做任何修改。
drawable-ldrtl-xhdpi
drawable-xhdpi
分辨率是一一对应的
当然了,除了对图像做预处理外,要是想用代码直接控制也是可以的。
private ImageView image2;
// 省略
image2 = (ImageView) findViewById(R.id.image2);
Drawable arrow = getResources().getDrawable(R.drawable.arrow);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (arrow != null) {
arrow.setAutoMirrored(true);
}
}
image2.setImageDrawable(arrow);
因为业务需要,以下代码均以Youtube网站在线视频为例
实现功能:
1、初始化的时候显示标题和视频封面
2、初始化的时候显示一个play按钮
3、不需要依赖任何SDK,或者导入任何第三方库
4、播放过程中可以暂停,可以拖动进度条快进
5、可以全屏播放
6、切换页面的时候会自动暂停
7、页面退出的时候自动销毁WebView
8、不需要申请任何开发者账号或者获取授权
原理:
首先需要一个继承WebView的自定义控件,这里起名叫做YoutubePlayerView,在页面初始化的时候用这个WebView去加载一个事先写好的HTML,当然在加载之前,需要把Youtube的视频id和一些播放参数设置进去。然后一个小的播放窗口就完成了,此时已经完成用户点击play按钮就播放的功能。
但是光能播放还不行,我们还需要捕捉用户的点击事件,比如播放,暂停等等操作,而这些操作本身写在Youtube的JS代码中(Youtube已经把JS调用相关代码的位置预留好,就等着开发者来复写相关的代码了),需要在JS代码中调用java代码,这样就需要有一个JS调用java的接口,这里起名叫QualsonBridge,通过使用WebVIew的addJavascriptInterface()方法将Java代码的接口设置进去,并且需要一个接口实现类,实现的方法名称方法要和JS接口规定的方法一模一样,以便反射调用,一会会把详细的代码贴出来。
完成以上两点,就已经完成了播放,暂停等操作,但是还需要在Activity退出或者被覆盖的时候暂停WebView的播放,所以还需要给这个WebView写一个onDestroy的方法,并在fragment的onDestroy中调用,里面执行的主要就是清楚缓存的操作,还需要WebView写一个onPause的方法,在fragment的onPause中调用,里面主要执行JS代码:javascript:onVideoPause()
关于全屏播放:是通过一个自定义的WebChromeClient来实现将WebView扩大到全屏并修改旋转角度进行播放
代码实现:
首先需要让WebView去加载一块HTML,这段HTML是从Youtube的官方SDK中抽取出来的,本质上是一个HTML编写的小播放窗口,代码如下
代码如下 | 复制代码 |
<!DOCTYPE html> function onPlayerReady(event) { var timerId = 0; function onPlayerPlaybackRateChange(playbackRate) { function onPlayerError(e) { function onPlayerApiChange() { function onReady(e){ function onPlaybackQualityChange(e){ function onPlaybackRateChange(e){ function onError(e){ function onApiChange(e){ function setCurrentSeconds(){ function sendDuration(){ function setLog(msg){ function onSeekTo(startSeconds){ function onVideoPause(){ function onVideoStop(){ function onVideoPlay(){ function onHideControls(){ function loadVideo(videoId, startSeconds){ function cueVideo(videoId){ |
项目中把这一段代码放到了raw文件夹下,并通过下面这样一个方法去加载,并在加载的同时,把上面预留的一些参数比如视频id什么的给补上
代码如下 | 复制代码 |
/** while ((read = buffer.readLine()) != null) { in.close(); String html = sb.toString().replace("[VIDEO_ID]", videoId).replace("[BG_COLOR]", backgroundColor); DdRwiH4mR0Q就是videoId
@JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface |
向js注册这个JAVA接口使用WebView的addJavascriptInterface(bridge, "QualsonInterface");方法
这里面的youTubeListener是自己写的一个接口类,用于方便回调UI方法的
下面贴出这个自定义WebView的完整代码:
代码如下 | 复制代码 |
package com.imaginato.qravedconsumer.widget.player; import android.annotation.SuppressLint; import com.qraved.app.R; import java.io.BufferedReader; public class YoutubePlayerView extends WebView { private static final String TAG = YoutubePlayerView.class.getSimpleName(); private QualsonBridge bridge = new QualsonBridge(); private YTParams params = new YTParams(); private YouTubeListener youTubeListener; public YoutubePlayerView(Context context) { public YoutubePlayerView(Context context, AttributeSet attrs) { @SuppressLint("JavascriptInterface") if (webChromeClient != null) { this.mPlayState = STATE.UNSTARTED; } public void initialize(String videoId, YTParams params, YouTubeListener youTubeListener, WebChromeClient webChromeClient) { public void setWhiteBackgroundColor() { public void setAutoPlayerHeight(Context context) { /** public STATE getPlayerState(){ public void play() { private void notifyStateChange(STATE state){ /** @JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface @JavascriptInterface
static { public void onDestroy() { private class MyWebViewClient extends WebViewClient { public MyWebViewClient(Activity activity) { @Override @Override public interface YouTubeListener { void onStateChange(STATE state);//暂停等等状态 void onPlaybackQualityChange(String arg);//清晰度改变 void onPlaybackRateChange(String arg); void onError(String arg); void onApiChange(String arg); void onCurrentSecond(double second); void onDuration(double duration); void logs(String log); public enum STATE { /** while ((read = buffer.readLine()) != null) { in.close(); String html = sb.toString().replace("[VIDEO_ID]", videoId).replace("[BG_COLOR]", backgroundColor); 在Fragment中初始化的代码
View youtubeView = LayoutInflater.from(journalActivity).inflate(R.layout.layout_youtube_player, null);
<com.xxx.YoutubePlayerView <FrameLayout </FrameLayout>
@Override @Override // 1. Stash the current state // 2. Stash the custom view callback // 3. Add the custom view to the view hierarchy // 4. Change the state of the window @Override // 2. Restore the state to it's original form // 3. Call the custom view callback } 上面提到的R.layout.view_layout_loading布局文件如下,仅仅是一个progressBar当占位符用的
<ProgressBar </FrameLayout>
当切换到其他fragment或者有新的Activity压到上面的时候暂停WebView的播放,fragment总的onPause方法这么写:
if(playerViewList!=null){
|
这里面的mCustomViewCallback 是WebChromeClient的onShowCustomView()方法的第二个参数,使用一个全局变量把它引用起来
相关文章
php 中file_get_contents超时问题的解决方法
file_get_contents超时我知道最多的原因就是你机器访问远程机器过慢,导致php脚本超时了,但也有其它很多原因,下面我来总结file_get_contents超时问题的解决方法总结。...2016-11-25- 相信很多站长都遇到过这样一个问题,访问页面时出现408错误,下面一聚教程网将为大家介绍408错误出现的原因以及408错误的解决办法。 HTTP 408错误出现原因: HTT...2017-01-22
- 下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
- php如何实现抓取网页图片,相较于手动的粘贴复制,使用小程序要方便快捷多了,喜欢编程的人总会喜欢制作一些简单有用的小软件,最近就参考了网上一个php抓取图片代码,封装了一个php远程抓取图片的类,测试了一下,效果还不错分享...2015-10-30
- ps软件是现在非常受大家喜欢的一款软件,有着非常不错的使用功能。这次文章就给大家介绍下ps把文字背景变透明的操作方法,喜欢的一起来看看。 1、使用Photoshop软件...2017-07-06
intellij idea快速查看当前类中的所有方法(推荐)
这篇文章主要介绍了intellij idea快速查看当前类中的所有方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-09-02- 1.在没有设置默认值的情况下: 复制代码 代码如下:SELECT userinfo.id, user_name, role, adm_regionid, region_name , create_timeFROM userinfoLEFT JOIN region ON userinfo.adm_regionid = region.id 结果:...2014-05-31
js导出table数据到excel即导出为EXCEL文档的方法
复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta ht...2013-10-13- 本文涉及的主题虽然很基础,在许多人看来属于小伎俩,但在JavaScript基础知识中属于一个综合性的话题。这里会涉及到对象属性的封装、原型、构造函数、闭包以及立即执行表达式等知识。公有方法 公有方法就是能被外部访问...2015-11-08
- 批量更新mysql更新语句很简单,更新一条数据的某个字段,一般这样写:复制代码 代码如下:UPDATE mytable SET myfield = 'value' WHERE other_field = 'other_value';如果更新同一字段为同一个值,mysql也很简单,修改下where即...2013-10-04
- ps软件是一款非常不错的图片处理软件,有着非常不错的使用效果。这次文章要给大家介绍的是ps怎么制作倒影,一起来看看设计倒影的方法。 用ps怎么做倒影最终效果̳...2017-07-06
Android开发中findViewById()函数用法与简化
findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20- 单个字符分割 string s="abcdeabcdeabcde"; string[] sArray=s.Split('c'); foreach(string i in sArray) Console.WriteLine(i.ToString()); 输出下面的结果: ab de...2020-06-25
- 最近想自学PHP ,做了个验证码,但不知道怎么搞的,总出现一个如下图的小红叉,但验证码就是显示不出来,原因如下 未修改之前,出现如下错误; (1)修改步骤如下,原因如下,原因是apache权限没开, (2)点击打开php.int., 搜索extension=ph...2013-10-04
- 如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
安卓手机wifi打不开修复教程,安卓手机wifi打不开解决方法
手机wifi打不开?让小编来告诉你如何解决。还不知道的朋友快来看看。 手机wifi是现在生活中最常用的手机功能,但是遇到手机wifi打不开的情况该怎么办呢?如果手机wifi...2016-12-21连接MySql速度慢的解决方法(skip-name-resolve)
最近在Linux服务器上安装MySql5后,本地使用客户端连MySql速度超慢,本地程序连接也超慢。 解决方法:在配置文件my.cnf的[mysqld]下加入skip-name-resolve。原因是默认安装的MySql开启了DNS的反向解析。如果禁用的话就不能...2015-10-21- javascript控制页面控件隐藏显示的两种方法,方法的不同之处在于控件隐藏后是否还在页面上占位 方法一: 复制代码 代码如下: document.all["panelsms"].style.visibility="hidden"; document.all["panelsms"].style.visi...2013-10-13
- 夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
- 本篇文章是对C#方法进行了详细的总结与介绍,需要的朋友参考下...2020-06-25