实现效果如下:
其中利用TextView和CircleImageView自定义出上面的按钮,并且利用java代码实现了此导航页的简单屏幕适配。之所以写这篇博客主要是总结一下在这个过程中所用到的技术以及所遇到的困难,若是大家对我描述的内容有何疑问、意见或建议,我都十分乐意与大家探讨,若是有何错误的地方,请大家批评指正。
一、利用现有控件组合出新的控件(TextView+CircleImageView = CirclePicText)
这新的带有图片和文字的控件取名为CirclePicText,下面是控件的创建过程。
(1)创建一个名叫imag_text_bt的xml文件,如下:
RelativeLayout 的长宽就是最终控件的尺寸,都是150dp。
对于TextView字体大小20sp,颜色白色(#FFFFFF)。
对于CircleImageView,长宽均是70dp,有一点值得关注的是对背景的设置,这个bt_bg_normal是一个xml文件,具体的后面再详细描述。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="150dp"
android:layout_height="150dp"
>
<TextView
android:id="@+id/image_text"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:textColor="#FFFFFF"
android:textSize="20sp"
android:layout_marginLeft="15dp"
/>
<de.hdodenhof.circleimageview.CircleImageView
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_toRightOf="@+id/image_text"
android:id="@+id/image_pic_view"
android:layout_centerVertical="true"
android:background="@drawable/bt_bg_normal"
/>
</RelativeLayout>
(2)创建一个名叫CirclePicText的类,继承自RelativeLayout。
具体如下(具体的设置过程都写在程序注释中了):
/*
*
*类描述:
*创建人:R
*创建时间:${DATA}16:06
*
*/public class CirclePicText extends RelativeLayout {
//定义一个TextView和CircleImageView控件,用以获取xml中的相应控件
private TextView pictextView;
private CircleImageView piccircleImageView;
//下面是两个构造函数
public CirclePicText(Context context) {
super(context);
}
/*至于为什么将加载布局、获取控件等一系列操作放置于下面这个构造函数
*主要是因为我们是在xml布局中使用CirclePicText,同时会为这个控件设置一些属
*最终这些属性需要通过attrs这个变量进行传递。所以会调用下面这个构造函数。
*/
public CirclePicText(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.imag_text_bt,this,true);//TODO:实验去掉ATTACH
/*当attachtoroot是true时,会把加载的layout布局返回的view控件加入到this,并且返回root布局。
* 如果当attachtoroot是false时,则会直接返回layout,不将view添加到this中,但是layout的参数会起作用
*如果root传入的是null,则最后得到的layout的layout_width layout_height属性不会起作用
*/
piccircleImageView =(CircleImageView)findViewById(R.id.image_pic_view);
pictextView = (TextView)findViewById(R.id.image_text);
Log.i("circle","createcircle");
setClickable(true);//layout默认是不可点击的 TODO:待会儿可以试验一下是不是监听器可以将控件变成可点击的 测试成功:监听器可以将控件变成可点击的
setFocusable(true);
}
/*这个函数用于设置TextView的内容、CircleImageView显示的图片以及
*CircleImageView的背景颜色,主要在主活动的Oncreate()中被调用。
*/
public void setText_Image(int pic, String name, String color)
{
pictextView.setText(name);
piccircleImageView.setImageResource(pic);
setImageBackgroundColor(piccircleImageView,color);
}
public void setImageBackgroundColor(View view, String color)
{
if(view ==null)
return;
GradientDrawable gradientDrawable = (GradientDrawable)view.getBackground();
gradientDrawable.setColor(Color.parseColor(color));
}
}
(3)完成上述步骤后就可以在布局文件中调用这一个控件了
具体的调用如下所示(下面xml取名first_nav_main):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/first_nav_bg"
android:id="@+id/nav_relative"
>
<!--为了设备的兼容性,考虑实时的对设备的屏幕大小进行测量,进而设置控件到边框的距离-->
<ren.define.circlepictext.CirclePicText
android:id="@+id/circlepic_lefttop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/layout_bg_selector"/>
<!--这里需要设置靠右边框的距离,所以需要将alignParentRight设置为true,以便右对齐-->
<ren.define.circlepictext.CirclePicText
android:id="@+id/circlepic_righttop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/layout_bg_selector"
android:layout_alignParentRight="true"/>
<ren.define.circlepictext.CirclePicText
android:id="@+id/circlepic_leftmid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/layout_bg_selector"
android:layout_marginTop="30dp"
android:layout_below="@+id/circlepic_lefttop"
/>
<ren.define.circlepictext.CirclePicText
android:id="@+id/circlepic_rightmid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/layout_bg_selector"
android:layout_alignParentRight="true"
android:layout_below="@+id/circlepic_righttop"
android:layout_marginTop="30dp"
/>
<ren.define.circlepictext.CirclePicText
android:id="@+id/circlepic_leftdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/layout_bg_selector"
android:layout_marginTop="30dp"
android:layout_below="@+id/circlepic_leftmid"
/>
<ren.define.circlepictext.CirclePicText
android:id="@+id/circlepic_rightdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/layout_bg_selector"
android:layout_below="@+id/circlepic_rightmid"
android:layout_marginTop="30dp"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
布局的效果如下:
(4)上述布局完成后需要在主活动的onCreate()函数中为这六个控件添加文字、图片以及背景色。
在这里再为大家推荐一个阿里的图库,里面有海量的图标任你随意下载:
https://www.iconfont.cn/collections/index?spm=a313x.7781069.1998910419.3
然后咱接着说
我在主活动中定义了四个数组CirclePicText(接收从xml中获取的控件)、circlepicID(控件ID)、imageDrawable(控件需要的图片)和color(控件的背景色)、name(控件文字),之后采用for循环进行设置。
CirclePicText(接收从xml中获取的控件)主要是用于为控件设置监听器以及响应。
package com.ronda.bluetoothassist;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import android.widget.Toast;
import de.hdodenhof.circleimageview.CircleImageView;
import ren.define.circlepictext.CirclePicText;
public class NavPageActivity extends AppCompatActivity {
private CirclePicText[] circlePic =new CirclePicText[6];
private int screenWidth;
private int screenHight;
//导航页的数据
private int[] circlepicID={R.id.circlepic_lefttop,R.id.circlepic_leftmid,R.id.circlepic_leftdown,
R.id.circlepic_righttop,R.id.circlepic_rightmid,R.id.circlepic_rightdown};
private int[] imageDrawable={R.mipmap.setting_nav,R.mipmap.mach_nav,R.mipmap.about_nav,
R.mipmap.light_nav,R.mipmap.history_nav,R.drawable.ic_backup};
private String[] name={"设置IP","设备","关于","安全警报","历史数据","备份"};
private String[] color ={"#66CCCC","#FFCCCC","#FF9999","#CCCCFF","#CC99CC","#FFCC99"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.first_nav_main);
//测量屏幕的尺寸(dp)
WindowManager windowManager = (WindowManager)this.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
screenWidth = (int)(displayMetrics.widthPixels/displayMetrics.density);
screenHight = (int)(displayMetrics.heightPixels/displayMetrics.density);
Log.i("circle","width:"+displayMetrics.widthPixels+"hight:"+displayMetrics.heightPixels+"density"+displayMetrics.density);
//设置图片、文字和颜色
for(int i=0;i<6;i++) {
circlePic[i] = (CirclePicText) findViewById(circlepicID[i]);
circlePic[i].setText_Image(imageDrawable[i],name[i],color[i]);
}
//利用setPadding设置RelativeLayout中控件到其边缘的距离
RelativeLayout relativeLayout = (RelativeLayout)findViewById(R.id.nav_relative);
relativeLayout.setPadding((int)(0.4*displayMetrics.density*(screenWidth-300)),(int)(0.5*displayMetrics.density*(screenHight-510)),(int)(0.4*displayMetrics.density*(screenWidth-300)),(int)(0.5*displayMetrics.density*(screenHight-510)));
//setPadding参数的单位是像素
//设置IP按钮的监听器
circlePic[0].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(NavPageActivity.this,"点击了设置IP",Toast.LENGTH_LONG).show();
}
});
//设备按钮的监听器
circlePic[1].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(NavPageActivity.this,"点击了设备",Toast.LENGTH_LONG).show();
}
});
//关于按钮的监听器
circlePic[2].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(NavPageActivity.this,"点击了关于",Toast.LENGTH_LONG).show();
}
});
//安全警报按钮的监听器
circlePic[3].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(NavPageActivity.this,"点击了安全警报",Toast.LENGTH_LONG).show();
}
});
//历史数据按钮的监听器
circlePic[4].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(NavPageActivity.this,"点击了历史数据",Toast.LENGTH_LONG).show();
}
});
//备份按钮的监听器
circlePic[5].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(NavPageActivity.this,"点击了备份",Toast.LENGTH_LONG).show();
}
});
}
}
(5)主要的设置说完了,我们再说说简单的屏幕适配(知识点:测量设备屏幕大小,动态设置间距)
我们可以看到上面的布局效果图和我们文章最前面看到的效果不一样呢,因为我们为了适配不同大小的屏幕,在java程序中对布局进行了一些动态的更改。
我所采用的屏幕适配的方法比较简单,无论屏幕多大,我们必须保证这六个控件位于屏幕中央。所以我采用的方法是在创建视图时测量设备屏幕的大小,然后根据屏幕的大小,调用setPadding()函数设置CirclePicText到屏幕边缘的距离。
下面是具体的方法:
WindowManager windowManager = (WindowManager)this.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
screenWidth = (int)(displayMetrics.widthPixels/displayMetrics.density);
screenHight = (int)(displayMetrics.heightPixels/displayMetrics.density);
采用上述的程序我们可以得到屏幕的大小,screenWidth和screenWidth分别是屏幕的宽和高,单位是dp。通过displayMetrics.widthPixels可以得到以px为单位的屏幕宽度,displayMetrics.heightPixels是以px为单位的屏幕高度,两者除以density(屏幕密度)换算成dp单位。若是读者对于这一换算不清楚可以看我的上一篇文章:
https://blog.csdn.net/RCY159/article/details/87867419 (单位辨析dp px sp)
得到屏幕的大小以后我就调用setPadding()进行设置,其中300(150dp+150dp)是两个控件的宽度,510(150dp+30dp+150dp+30dp+150dp)是三个控件加上两个空隙的高度。另外0.5则是假设控件到屏幕的上下边的距离相等推算出来的,0.4是假设控件到屏幕的左右边的距离相等推算出来的。
//利用setPadding设置RelativeLayout中控件到其边缘的距离
RelativeLayout relativeLayout = (RelativeLayout)findViewById(R.id.nav_relative);
relativeLayout.setPadding((int)(0.4*displayMetrics.density*(screenWidth-300)),(int)(0.5*displayMetrics.density*(screenHight-510)),(int)(0.4*displayMetrics.density*(screenWidth-300)),(int)(0.5*displayMetrics.density*(screenHight-510)));
//setPadding参数的单位是像素
(6)最后说说之前控件的背景设置(知识点:selector、shape、以及CircleImageView的背景设置问题)
一般背景都只是简单的设置颜色,但是如果你在imag_text_bt.xml中直接为CircleImageView设置颜色背景,实际的效果会如下图所示。
因为CircleImageView本身背景就是方块,所以我们不止要设置背景颜色还要修改控件的形状。所以我们采用bt_bg_normal.xml作为背景。这个xml中具体有什么,让我们来看一看…
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="35dp"/>
<solid android:color="#FFFFFF" />
<stroke android:width="1dp" android:color="@color/colorAccent"/>
</shape>
corners属性为圆角的半径,solid为背景颜色,stroke为控件的边框,里面是边框的一些属性,包括边框宽度和边框线条颜色。
以上说的是CircleImageView这个控件的背景设置,也就是圆圈里图片的背景颜色。
下面说说我们自己定义的控件CirclePicText的背景设置,我们从那个动图可以看到,我们点击按钮时按钮的颜色会由浅变深,这一变化也要通过背景设置来实现,这设置和上面的比较相似,但也有区别。我们可以看到,在first_nav_main.xml中六个控件的背景都设置成了
android:background="@drawable/layout_bg_selector"
这个layout_bg_selector也是个xml文件,具体如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/layout_bg_normal" android:state_pressed="false"/>
<item android:drawable="@drawable/layout_bg_pressed" android:state_pressed="true"/>
</selector>
从上面的xml代码我们可以很清晰的看到,当控件是按下状态时背景是加载的layout_bg_pressed文件,当非按下状态时背景是加载layout_bg_normal文件,也就是在不同的状态下加载不同的背景文件。
这两个背景文件如下:
ayout_bg_pressed
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="15dp"/>
<solid android:color="#25000000"/>
</shape>
ayout_bg_normal
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="15dp"/>
<solid android:color="#12000000 "/>
</shape>