一、自定义控件
1.1 基础知识
1.大小、长度单位
px :像素点 —— 一般不用做长度单位,因为不同型号的手机的像素密度是不一样的,以它为单位的话会造成控件在不同手机的长度不一样;
dp:与像素点密度密切相关 —— dp可以使不同型号的手机显示基本相同的长度;
sp:相当于dp(用来修饰文字)
dip = dp
使用方法:文字的尺寸一律用sp单位;
非文字的的尺寸一律用dp单位;
偶尔使用px单位,例如需要的屏幕上画一条细的分割线:1px
2.Inflater
Inflater就是把一个xml的布局文件解析成一个View的过程
获得inflater实例的方法
getLayoutInflater();
getSystemService(LAYOUT_INFLATER_SERVICE);
LayoutInflater.from(context);
3.提取布局属性Theme&Style
何时使用Theme和Style?
对多个页面或控件有一些相同的属性,可以提取出这些属性,放在style.xml中,需要时直接提取出来(使用style=@style/ xxxx 语句);
Theme主题:在AndroidManifest.xml中的app全局设置,也可以在每个Activity中设置(使用android:theme = @style/ xxxx 语句);
父布局的使用:style时可以继承的,和类的继承相似。
1.2 View是如何工作的
构造器——>初始化
onMesure();定大小
onLayout();定位置
onDraw(); 绘制
invalidate(); 刷新
1.3 自定义控件的三种形式:
继承已有控件来实现自定义控件
继承一个布局文件实现自定义控件
继承一个View类实现自定义控件
canvas的使用方法:
当要在一个Paint上draw出两个或以上的图形时,先设置一个图形的paint属性,用canvas画出来之后,再重新设置下一个图形的paint属性,再用canvas画出来。这就相当于,canvas是画家,而paint是客户为画家提的要求。(比喻好垃圾,不好意思,语文是体育老师教的)protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 设置circle的属性 mPaint.setColor(Color.RED); canvas.drawCircle(getWidth()/2,getHeight()/2,getWidth()/2,mPaint); //canvas先draw出circle之后,再改变paint的属性设置,去draw text mPaint.setTextSize(200); mPaint.setColor(Color.WHITE); String text = String.valueOf(mNumber); mPaint.getTextBounds(text,0,text.length(),mRect); int textWidth = mRect.width(); int textHeight = mRect.height(); canvas.drawText(text,getWidth()/2-textWidth/2,getHeight()/2+textHeight/2,mPaint); }
1.4 如何自定义视图属性:
1.在哪里创建属性?
找到res—>values创建attrs.xml文件,然后创建attr属性。
<declare-styleable name="TestRedButton">
<attr name="backgroundColor" format="color"/>
<attr name="textColor" format="color"/>
<attr name="textSize" format="dimension"/>
</declare-styleable>
注意:创建属性时,每个属性对应的单位(类型)要与引用这些属性时的类型一致。如:color对应的类型是color,textSize对应的类型是int / dimension/ interger等。
2.如何使用这些属性?
· 代码中引用——在初始化视图中,定义一个TypedArray用来装各个属性,然后可以设置每个属性的默认属性,接下来可以在代码中直接调用这个属性。
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.TestRedButton);
// 设置背景颜色默认为红色,字体大小默认值为18
mBackgrounColor = typedArray.getColor(R.styleable.TestRedButton_backgroundColor,Color.YELLOW);
mTextSize = typedArray.getDimensionPixelSize(R.styleable.TestRedButton_textSize,18);
//调用该属性
mPaint.setColor(mBackgrounColor);
· 布局中引用——在布局文件中,自定义一个命名空间(例中为app)
xmlns:app ="http://schemas.android.com/apk/res-auto"接下来就可以用app直接调用该属性
app:textSize="18dp"
二、大名鼎鼎的Fragment
1.1 基础知识
1. 什么是Fragment?
Fragment相当于一段模块化的Activity,它具有自己的生命周期,接收自己的事件,在Activity运行时添加或删除。
2.为什么使用Fragment?
支持更加动态灵活的界面设计;
可以在平板灯大屏幕设备上使用;
Activity的Layout可以分成几个Fragment。
3. Fragment的生命周期
1.2 加载Fragment
1.2.1 静态加载
1. 新建Fragment的布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@color/colorButton">
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/id_title_left_btn"
android:background="@drawable/ic_launcher"
android:layout_marginLeft="3dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="我不是微信"
android:textColor="@color/white"
android:id="@+id/id_title_right_text"
android:gravity="center"
android:textSize="20sp"
android:textStyle="bold"
/>
</RelativeLayout>
2.新建对应的Fragment类
public class TitleFragment extends Fragment {
private ImageView mLeftMenu;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.titlefragment_layout,container,false);
mLeftMenu = (ImageView)view.findViewById(R.id.id_title_left_btn);
mLeftMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getActivity(),"我的个人资料",Toast.LENGTH_SHORT).show();
}
});
return view;
}
}
3.修改main_activity.xml中的代码
<?xml version="1.0" encoding="utf-8"?>
<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">
<fragment
android:layout_width="match_parent"
android:layout_height="60dp"
android:id="@+id/id_fragment_title"
android:name="com.example.administrator.exerciseview.TitleFragment"
tools:layout="@layout/titlefragment_layout"/>
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/id_fragment_content"
android:name="com.example.administrator.exerciseview.ContentFragment"
android:layout_below="@+id/id_fragment_title"
android:layout_above="@+id/id_ly_bottombar"
tools:layout="@layout/contentfragment_layout"/>
</RelativeLayout>
注意:这里还需要通过 android:name 属性来显式指明要添加的碎片类名,一定要将类的包名也加上。
1.2.2动态加载
- 创建待添加Fragment实例;
- 获取到FragmentManager,在活动中可以直接调用getFragmentManager()方法得到;
- 开启一个事务,用beginTransaction()开启;
- 向容器内加入Fragment,一般用replace来实现,需要传入容器的id和待添加Fragment的实例;
- 提交事务,调用commit()方法完成。
public class FragmentActivity extends Activity implements View.OnClickListener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_fragment);
Button button = (Button) findViewById(R.id.button_weixin);
button.setOnClickListener(this);
setDefaultFragment();
}
public void setDefaultFragment(){
FragmentManager fm = getFragmentManager(); //获取FragmentManager
FragmentTransaction ft = fm.beginTransaction(); //开启一个事务
ContentFragment contentFragment = new ContentFragment();
ft.replace(R.id.content_frame_layout,contentFragment).commit();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button_weixin:
WeixinContentFragment weixinContentFragment = new WeixinContentFragment();
ContentFragment contentFragment = new ContentFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.content_frame_layout,weixinContentFragment);
ft.commit();
break;
default:
break;
}
}
}
1.2.3 返回栈
FragmentTransaction 中提供了一个 addToBackStack()方法,可以用于将一个事务添加到返回栈中,它可以接收一个名字用于描述返回栈的状态,一般传入 null 即可。
fragmentTransaction.addToBackStack(null);
1.2.4 碎片和活动之间的通信
- 为了方便碎片和活动之间进行通信,FragmentManager 提供了一个类似于findViewById()的方法,专门用于从布局文件中获取碎片的实例,代码如下:
<span style="white-space:pre"> </span>RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);
2. 在碎片中又该怎样调用活动里的方法呢?其实这就更简单了,在每个碎片中都可以通过调用 getActivity()方法来得到和当前碎片相关联的活动实例,代码如下所示:
MainActivity activity = (MainActivity) getActivity();
1.3 使用限定符
1.什么是限定符?(Qualifiers)
如果你经常使用平板电脑,应该会发现很多的平板应用现在都采用的是双页模式(程序会在左侧的面板上显示一个包含子项的列表,在右侧的面板上显示内容),因为平板电脑的屏幕足够大,完全可以同时显示下两页的内容,但手机的屏幕一次就只能显示一页的内容,因此两个页面需要分开显示。那么怎样才能在运行时判断程序应该是使用双页模式还是单页模式呢?这就需要借助限定符来实现了。
2.使用方法
- 在layout文件夹中创建一个正常的页面布局(一般是适用于手机的单页布局);
- 在res文件夹中创建一个large-layout文件夹,在此文件夹中创建一个适用于大屏的双页布局(新创建的布局文件和layout中对应的文件名相同);
- 其中large是一个限定符,当屏幕被认定为large的设备就会自动加载large-layout中的布局文件。
3.Android中常见的限定符
4. 使用最小宽度限定符
有的时候我们希望可以更加灵活地为不同设备加载布局,不管它们是不是被系统认定为“large”,这时就可以使用最小宽度限定符(Smallest-width Qualifier)了。
最小宽度限定符允许我们对屏幕的宽度指定一个最小值(以 dp 为单位),然后以这个最小值为临界点,屏幕宽度大于这个值的设备就加载一个布局,屏幕宽度小于这个值的设备就加载另一个布局。
如在 res 目录下新建 layout-sw600dp 文件夹,然后在这个文件夹下新建activity_main.xml 布局,表示当程序运行在屏幕宽度大于 600dp 的设备上时,会加载layout-sw600dp/ activity_main 布局。
拾遗:
1. xml文件的第一行代表xml的属性:
如 <?xml version="1.0"encoding="utf-8"?> 表示版本号是1.0,编码是utf-8
xmlns表示xml的name space(命名空间)
自定义命名空间,如:
xmlns:app ="http://schemas.android.com/apk/res-auto" 表示声明了一个名为app的命名空间
3. 编译单元:一个 “.Java” 文件(源代码)。一个编译单元可以有一个或多个类,但只有一个类是public,且类的名字和源代码文件名相同。