Android Font Basic

文字最终的大小是和绘制文字的字体的类型和字体的大小是相关的.

设置字体类型 Paint.setTypeface(Typeface typeface)

设置字体大小 Paint.setTextSize(float textSize)

几个相关的尺寸和方法

Paint.FontMetrics

有5个属性,并且这5个属性都是跟字体相关的.

top: 字体中字符超出基线之上的最大距离
bottom: 字体中字符超出基线之下的最大距离
ascent: 单个字符超出基线之上的推荐距离
descent: 单个字符超出基线之上的推荐距离
leading: 标准行间距

Paint.getTextBounds

这个方法获取字符[字符串]占据的矩形区域,意为字体可见部分的矩形区域

1
2
3
4
Rect bound = new Rect();
mPaint.getTextBounds(text, 0, text.length(), bound);

bound.right - bound.left //得到的就是字符[字符串]的可见部分矩形区域的宽度

Paint.measureText(text)

返回的是字符[字符串]的宽度,注意与

1
bound.right - bound.left

相互区分.

因为通常来说每个字符两边都会留有一部分空白区域,便于阅读.所以measureText的尺寸通常会大于bound.right - bound.left.所以,对于单个字符来说:

1
measureText = bound.right - bound.left + 字符两边的留白宽度

整体图示如下

1

应用场景

  1. 垂直居中的文字,计算基线位置使用FontMetrics比较方便.

  2. 或者大小不一的问题要实现对齐,使用getTextBounds比较方便.

  3. 获取文字的理想宽度,使用measureText比较方便

图示代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Rect bound = new Rect();

@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setTextSize(600);
String text = "p";
draw1(canvas, text, 800);
}

private void draw1(Canvas canvas, String text, float baseLineY) {

mPaint.getTextBounds(text, 0, text.length(), bound);
mPaint.setColor(Color.rgb(0xb4, 0xb4, 0xb4));
canvas.drawRect(bound.left, bound.top + baseLineY, bound.right, bound.bottom + baseLineY, mPaint);

float width = mPaint.measureText(text);
mPaint.setColor(Color.rgb(0x00, 0x00, 0x00));
canvas.drawRect(bound.left, bound.top + baseLineY - 30, bound.left + width, bound.top + baseLineY - 20, mPaint);

Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
mPaint.setColor(Color.BLACK);
canvas.drawText(text, 0, baseLineY, mPaint);
canvas.drawLine(0, baseLineY, getWidth(), baseLineY, mPaint);
mPaint.setColor(Color.RED);
canvas.drawLine(0, baseLineY + fontMetrics.top, getWidth(), baseLineY + fontMetrics.top, mPaint);
canvas.drawLine(0, baseLineY + fontMetrics.bottom, getWidth(), baseLineY + fontMetrics.bottom, mPaint);
mPaint.setColor(Color.BLUE);
canvas.drawLine(0, baseLineY + fontMetrics.ascent, getWidth(), baseLineY + fontMetrics.ascent, mPaint);
canvas.drawLine(0, baseLineY + fontMetrics.descent, getWidth(), baseLineY + fontMetrics.descent, mPaint);

}

Javascript TouchSlide

一个简单的手机网页滑屏效果库

进行适当的CSS设置之后可以实现常见的全屏滑动

github地址:TouchSlide
git.oschina.net地址:TouchSlide

原理

检测上滑或者左右滑动事件,然后使用CSS3动画进行页面转换。

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

<div class="demo horizontal">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>

[script]

new TouchSlide(containerId or ) //default horizontal slide

or

new TouchSlide(containerId, {
'orientation': TouchSlide.VERTICAL
}) // vertical slide

效果图

vertical


horizontal

产品开发前的准备

以下所有操作基于企业主体,不要使用个人账号。  

申请企业邮箱

比如腾讯企业邮箱。  

申请各种账号

所有的手机号都应该绑定到创始人的手机号,推荐做法是创始人办理一个新独立的手机号,专门用于申请各类账号。后续的各种其他类型账号注册都基于这个独立的手机号。

项目 账号
基础账号
微信 创始人手机
支付宝 创始人手机
QQ 创始人手机
微博 创始人手机
头条 创始人手机
抖音 创始人手机
网站系
域名管理员 公司专用邮箱、创始人手机
云平台管理员 公司专用邮箱、创始人手机
腾讯系
微信公众平台 公司专用邮箱、创始人微信
微信开放平台 公司专用邮箱、创始人微信
微信商户平台 创始人微信
QQ开放平台 公司专用邮箱、创始人QQ
阿里系
支付宝开放平台 公司专用邮箱、创始人手机
开发平台
Apple开发者平台 公司专用邮箱、创始人手机
其他开放平台(小米、华为,Oppo等等) 公司专用邮箱、创始人手机
其他
代码管理平台 公司专用邮箱、创始人手机
头条、百度、微博等等 公司专用邮箱、创始人手机

 

变长参数的坑

VarArgs简述

只需要明确一点即可,java方法的变长参数只是语法糖,其本质上还是将变长的实际参数 varargs 包装为一个数组。

所以

Object[] objs

Object... objs

被看作是相同的签名,在源码级别是不能同时存在的,因此,无法编译通过

VarArgs包装

包装方式(注意,下面的代码不是实际的实现,而是一个比喻说明):

  1. 如果实参是唯一且匹配形参varargs要求的数组(就是实参独占形参varargs),那么可以认为你已经替编译器干了这个活,所以可以看作不包装,直接使用这个数组
1
2
3
4
5
6
7
public void invoke(String...varargs){}

invoke(new String[]{"a", "b", "c"});
那么大致的调用过程如下:
public void invoke(String...varargs){
innerVarArgs = varargs;
}
  1. 如果实参是多个单独的变量(就是多个实参分享varargs),那么会将这多个参数包装成一个相应的类型数组
1
2
3
4
5
6
7
8
9
10
11
public void invoke(String...varargs){}

invoke("a", "b", "c");

那么大致的调用过程如下:
public void invoke(String...varargs){
innerVarArgs = new String[varargs.length];
for(int i = 0, size = varargs; i < size; i++){
innerVarArgs[i] = varargs[i];
}
}

Read More

ListPopupWindow

ListPopupWindow最低要求为api11,为了兼容到2.1, 可以使用包含在support V7包中实现。
从效果上来讲,ListPopupWindow就是一个弹出层的ListView,比较适合用来实现自定义的下拉菜单以及自定义的下拉选择列表。

使用

自定义样式

一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<style name="V7.ListPopupWindowStyle" parent="@style/Widget.AppCompat.ListPopupWindow">
<item name="android:popupBackground">#404040</item> //弹出层的背景
<item name="android:dropDownVerticalOffset">0dip</item>
<item name="android:dropDownHorizontalOffset">0dip</item> //水平以及垂直位移
<item name="android:dropDownWidth">match_parent</item> //这个效果不大
</style>

<style name="V7.DropDownListViewStyle" parent="@style/Widget.AppCompat.ListView.DropDown">
<item name="android:listSelector">@drawable/list_selector</item> //
<item name="android:divider">#242424</item>
<item name="android:dividerHeight">1px</item>

... //其他列表样式
tyle>

AppTheme是应用到Activity的主题
listPopupWindowStyle 对应弹出层的主题样式
dropDownListViewStyle 对应内含列表的主题样式,与普通ListView的定制方式一致

<style name="V7.ListPopupWindow" parent="AppTheme">
<item name="listPopupWindowStyle">@style/V7.ListPopupWindowStyle</item>
<item name="dropDownListViewStyle">@style/V7.DropDownListViewStyle</item>
</style>

代码调用

实现微信右上角弹出菜单,使用方式与PopupWindow差不多:

1
2
3
4
5
6
7
8
9
10
11
ListPopupWindow listPopupWindow = new ListPopupWindow(this);
listPopupWindow.setAnchorView(view);
listPopupWindow.setWidth(300); //如果不设置宽度的话,默认取AnchorView的宽度,一般不是我们想要的结果
listPopupWindow.setModal(true); //是否为模态,影响到对back按钮的处理
listPopupWindow.setAdapter(new ArrayAdapter<String>(this, R.layout.apt_v7_list_popup_window, R.id.apt_v7_tv, new String[]{
"发起群聊",
"添加朋友",
"扫一扫",
"意见反馈"
}));
listPopupWindow.show();

与PopMenu的对比

  1. PopMenu难以定制,ListPopupWindow的定制性更好
  2. ListPopupWindow不能自适应宽度
  3. PopMenu以面向菜单为核心,可以更方便的实现 禁用/开启 功能

一个让ListPopupWindow自适应宽度的方案,设置adapter后,检测每一行的最大宽度,然后再来设置 ListPopupWindow 的宽度,有利有弊,自己取舍了。

关于菜单那的其他实现方式:

  1. PopMenu
  2. PopupWindow + 自定义ContentView
  3. 页面内View + 自定义touch事件以及按键事件处理

demo

demo

Java8 Optional

按照字面意思的理解,应该可选的意思。一开始我还以为是类似python里面的默认参数用法呢,结果语义是指某个值可能有也可能没有(null)。
感觉名字取得不是很直观。。我觉得叫Nullable不是更好?

python:

1
2
3
def fn(a='default_value'):
print(a)
pass

Read More

Android RecyclerView

RecyclerView 在v7.21+包中,是一个用来展示大量数据的组件,或者说,就是ListView的改善版本(注:现阶段的功能没有ListView完善,因此想完全取代ListView的话并不明智)。
相比ListView,RecyclerView的扩展性更好,因此也更适合与android新曾的组件配合使用,这样使用起来更得心应手。

RecyclerView与ListView的原理差不多,本质上都是以适配器为核心。只不过ListView缓存的是view,viewHolder附着在view上,而RecyclerView缓存的是viewHolder,view包含在viewHolder内。

如果我们将ListViewAdapter稍作修改,也可以实现这种设计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
abstract class LvAdapter<VH extends LvAdapter.ViewHolder> extends BaseAdapter {
public class ViewHolder {
private View view;
public ViewHolder(View view) {
this.view = view;
}
public View getView() {
return view;
}
}

@Override
public int getCount() {
return 0;
}

@Override
public Object getItem(int position) {
return null;
}

@Override
public long getItemId(int position) {
return 0;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
VH vh = getViewHolder();
onBindViewHolder(vh, position);
return vh.getView();
}

public abstract VH getViewHolder();

public abstract void onBindViewHolder(VH holder, int position);
}

这样有点不直观,所以更进一步,ListView的Adapter可以更抽象,将数据绑定方法交给viewHolder,这样adapter的工作就更单纯一点.

一个方便的ListViewAdapter可以参考base-adapter-helper

RecyclerView使用

适配器

既然都是以适配器为核心,因此,只需要写好适配器就行了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class RvViewHolder extends RecyclerView.ViewHolder {

public TextView tv;

public RvViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}

class RvAdapter extends RecyclerView.Adapter<RvViewHolder> {

Context context;
String[] items;

public RvAdapter(Context context, String[] items) {
this.context = context;
this.items = items;
}

@Override
public RvViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.apt_v7_recycler_view, parent, false);
return new RvViewHolder(view);
}

@Override
public void onBindViewHolder(RvViewHolder holder, int position) {
holder.tv.setText(items[position]);
}

@Override
public int getItemCount() {
return items.length;
}
}

rv.setAdapter(new RvAdapter(this, new String[]{
"A", "B", "C", "D",
"A", "B", "C", "D",
"A", "B", "C", "D",
"A", "B", "C", "D",
"A", "B", "C", "D",
"A", "B", "C", "D",
"A", "B", "C", "D",
"A", "B", "C", "D",
"A", "B", "C", "D",
"A", "B", "C", "D",
"A", "B", "C", "D",
"A", "B", "C", "D",
"A", "B", "C", "D"
}));
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayout.VERTICAL);
rv.setLayoutManager(linearLayoutManager);

布局管理器 与 横向列表

RecyclerView的另一个特点就是布局管理器(LayoutManager),个人觉得的一个比较方便的地方就是,终于可以方便的实现横向列表了!布局管理的作用就是负责摆放view以及回收view。
类似的

1
2
int findFirstVisibleItemPosition();
int findLastVisibleItemPosition();

方法也在布局管理器中

设置item动画

1
rv.setItemAnimator(ItemAnimator);

分割线

由于RecyclerView的布局不固定,因此没有像ListView那样直接设定divider的方式,这些可能需要自己实现。一个简单粗鲁的纵向列表分割线如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
rv.addItemDecoration(new RecyclerView.ItemDecoration() {

Paint paint = new Paint();

@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
}

@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
for (int i = 0, size = parent.getChildCount(); i < size; i++) {
View child = parent.getChildAt(i);
c.drawLine(child.getLeft(), child.getBottom(), child.getRight(), child.getBottom(), paint);
}
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
}
});

还可以参考:DividerItemDecoration

绑定事件

RecyclerView 没有 ListView 的 OnItemClick 等方法,所以,类似方法需要自己处理。

  1. 可以直接给itemView或者其子view绑定事件,绑定可以在viewHolder中进行事件绑定,也可以在adapter中进行。
    不过viewHolder中无法直观获取到原始的Item数据,需要进一步的处理(比如给itemView设置tag)才能实现获取原始数据的目的,所以怎么使用需要自己权衡。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class RvViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

public TextView tv;

public RvViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
itemView.setTag(this);
itemView.setOnClickListener(this);
}

@Override
public void onClick(View v) {
int vId = v.getId();
if (vId == R.id.tv) {
Toast.makeText(itemView.getContext(), getPosition() + "", Toast.LENGTH_SHORT).show();
}
}
}
  1. 可以使用
1
addOnItemTouchListener(RecyclerView.OnItemTouchListener listener)

来进行全局处理。当然,缺点就是需要处理事件细节,比较繁琐。

demo

demo

控制台输入小节

BufferedReader

最基础的方法,从System.in输入流中获取数据

1
2
3
4
5
6
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}

Scanner

1
2
Scanner sc = new Scanner(System.in);
System.out.println(sc.nextLine());

Console

java6新增

1
2
3
4
Console console = System.console();
if(console != null){
System.out.println(console.readLine());
}

说明

  1. Console主要是为了方便程序与用户交互,但是有一个限制就是需要使用系统自带的控制台来运行程序,如果在IDE中使用的话,就有很大的可能获取不到Console。

  2. Console的另一个特点就是可以隐藏密码:

1
2
3
4
Console console = System.console();
if(console != null){
System.out.println(console.readPassword());
}
这样就看不到用户输入的密码了,但是没有办法将用户的输入显示成“*”或者别的字符
  1. Scanner方便将用户的输入转换成对应的java类型,另外,Scanner的输入源不限于System.in,还可以是其他的输入源。

Android 9.png面面观

NinePatch Image就是平时说的9.png格式图片

优点:可以定义可扩展区域,自动处理png图片扩展的问题,可以很好的适应Android设备那千奇百怪的屏幕分辨率要求。

限制,只适合纯色或者简单线框的图形,如果涉及的图像比较复杂或者颜色层次较多,是难以获得比较良好的扩展效果的。

主要特点:只是在普通png图像最外面增加了一圈1px的边框,边框就是用来定义图片中可扩展的和静态的区域。对于大多数情况来说,9.png可以分割为9个区域,这大概也是9.png这个名称的由来:

1

如上图所示,上边和左边的黑色边框将图片分割成9个区域,其中

1,3,7,9是静态区域,也就是不会被扩展的区域。

2,8是水平扩展区域

4,6是竖直扩展区域

5是重合部分,也就是水平竖直均可以扩展的区域

当然,虽然叫9.png图片,但是并不只限于分割成9个区域,上边和左边可以定义任何数量的线框,从而将图片分割成多个区域。

另外,右边和底部边框可以定义内容区域,不过这个内容区域与是否扩展没有联系,更多只是像给内容增加一个padding而已:因此这部分是可选的。

2

二、draw9patch

draw9patch是android SDK自带的编辑9.png图片的小工具。使用比较简单,只有几个选项:

1.show lock。显示不可绘区域

2.show patches。显示分割区域

3.show content。显示内容区域,即预览图中的紫色区域

4.show/hide bad patches。显示有问题区域

唯一值得一提的是bad patches功能,可以预先标示出在扩展中会出现失真的区域:

3

放大看看:

4

可以看出,由于水平扩展区域覆盖了左上角的圆角,扩展过程中会产生失真,所以用红色方框标示出来了,这个功能算是比较有用的。

三、注意问题

  1. 9.png格式图片放在什么文件夹下面?

    9.png除了可以定义扩展区域外,其他与普通png没有区别,所以最好是按照hdpi,xhdpi,xxdpi文件夹放置。所以如果只是单纯的圆角矩形之类的图片,最好还是直接使用xml文件定义drawable比较方便。

  2. 9.png图片大了怎么办?

    如果9.png图片过大,可能出现不同区域相互覆盖的问题。所以9.png图片只需要保留那些主要轮廓,尽量做得小一些。

  3. 9.png图片显示有黑边框还在

    如果右边和下边的内容区域没有定义,某些时候就会出现这种问题,所以最好还是将右边和下边的内容区域全部都定义出来。