Android 使用 RecyclerView 分隔线问题

自从Google推出了RecyclerView作为ListView功能更加强大的替代品之后,越来越多的APP开始使用这个控件。RecyclerView可以优化程序性能,并且由于功能上的高度解耦,可以无成本地在ListView、GridView以及瀑布流之间切换,但是也正因为如此,原来在ListView中理所当然的东西在RecyclerView中要自己来实现,比如分隔线,比如OnItemClickListener等(不是特别理解为什么Listener还要我们自己实现,我的做法就是往Adapter里扔回调)

说到分隔线,可以去实现RecyclerView.ItemDecoration来定制,在鸿洋大大的博客里有非常详细的讲解,按照这个来做其实也不麻烦,而且一次编写复用终生,但是我做的时候还是想偷懒,不想去写那么多代码,所以想用背景图片来实现分隔线效果。

我的RecyclerView中每个item的背景是这样的,用了selector实现点击前后的变色效果

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/list_item_bg_pressed" android:state_pressed="true"/>
<item android:drawable="@drawable/list_item_bg_unpressed" android:state_pressed="false"/>
</selector>

其中@drawable/list_item_bg_pressed是这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 第一层,露出个小边当分隔线 -->
<item>
<shape android:shape="rectangle">
<solid android:color="@color/color_list_item_divider"/>
<padding android:bottom="@dimen/dimen_list_item_divider_height"/>
</shape>
</item>
<!-- 第二层,真正的背景色 -->
<item>
<shape android:shape="rectangle">
<solid android:color="@color/color_list_item_bg_pressed"/>
</shape>
</item>
</layer-list>

使用layer-list来实现效果:第一层使用分割线的颜色,并在底部padding一个窄边,第二层使用正常背景颜色,这样写而不是直接用一张图片代替的原因是:如果使用普通图片作为背景,当图片被拉伸时分隔线会明显变粗,效果非常不美观(当然.9图片没有问题,想了想还是直接用代码写了),而用代码就没有这个问题。这种效果我还用来实现一些输入框。

@drawable/list_item_bg_unpressed 同理,就是把第二层的颜色换了一下。

那么为什么不直接用一张图片来代替呢?因为有些情况下RecyclerView每个条目的高度并不是一样的,这样对于普通图片会产生纵向拉伸的效果,使得分隔线变得非常可怕,而用代码来实现的.9效果则不会有这个问题。

JavaScript 中的运算操作陷阱

最近做JSP大作业,需要写一个网站,其中用到了分页系统,就自己撸了一个。

其中的翻页逻辑是这样写的:

1
2
3
4
5
6
7
8
/*上一页*/
previousPage.click(function(){
clickPage(grids[currentActivePos].find('a').html() - 1);
});
/*下一页*/
nextPage.click(function() {
clickPage(grids[currentActivePos].find('a').html() + 1);
});

扔到网页里实验,发现[上一页]功能没有问题,但是点击[下一页]之后没有反应,下边还有一个类似的错误:

1
grids[i].find('a').html(index + i - 3);

当测试时,index是从.html()方法获取的字面值为3的参数,i是循环控制变量,当i也是3时,出现了gridsp[i]的值变成30而不是3的情况。

一直想不明白这奇怪的现象是因为什么,但问题肯定就出在这句话上,所以就试着把表达式的顺序变了一下,像这样:

1
grids[i].find('a').html(i - 3 + index);

发现结果变成了03,到这里才明白这结果不是数值而是字符串!也就是index是string类型的,直接用数字和它进行+操作结果不是进行数值运算而是字符串连接!

因而也明白了翻页逻辑的错误在哪里。改动如下:

1
2
3
4
5
6
7
8
9
10
11
/*上一页*/
previousPage.click(function(){
clickPage(grids[currentActivePos].find('a').html() - 1);
});
/*下一页*/
nextPage.click(function() {
var des = eval(grids[currentActivePos].find('a').html() + '+ i');
clickPage(des);
});
/*先进行减法运算,把表达式结果转换成数值类型*/
grids[i].find('a').html(index - 3 + i);

总结:非常傻的一个错误,找到原因之后觉得自己非常基础的东西没有掌握牢固。这一段时间虽然涉及的面不少,但是都没有什么深入,这样就有点像”非科班”的CS学生了,应该警醒。

Android ListView 和 ScrollView 冲突问题

最近做一款APP,其中有一个类似微博的评论功能的界面,先是列出微博的正文内容和图片等,然后下边是评论。一开始就想着用一个ScrollView把主要内容和评论区的ListView包起来,然后添加各个控件的内容即可(对,感觉上有点像这个CSDN博客的编辑界面嘛),但是写出来之后发现ListView只显示出了一个条目的高度,并且不能滑动,网上搜了一下发现原因是ScrollView和ListView都是可滑动的,把它们放在一块会有冲突,最后还是ScrollView获得了焦点,ListView不能滑动。网上的解决方法最多的是在加载ListView时用getMeasure计算每个条目和分割线的高度,然后相加,把结果设置为ListView控件的高度,不过貌似是只适用于ListView每个条目高度都一样的情况(没有试过,很奇怪为什么会这样)。要么就是自定义一个继承自ListView的控件,也是事先设置好ListView的高度,但这样总归比较麻烦,而且准确度不如由系统自己构造好。

懒癌发作实在不想自己去做这些事情,于是便想试一下比较投机的方法,就是在ListView的Adapter的getView方法中根据position构造不同的界面,即如果position是0,则用原来主要信息(微博正文,图片)的xml文件取inflate convertView,否则就用评论条目的xml去inflate,经试验果然可行。之后不死心想看下有没有更好的实现方法,去overflow上找了一下,发现有人推荐的方法和我的差不多,所以认为这种方法是比较好的,不需要做额外的工作,只需要把inflate的工作由主Activity放在Adapter里就可以了。

getView方法

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
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
/*主信息界面*/
if(0 == position)
{
MainHolder holder = null;
convertView = inflater.inflate(R.layout.info, parent, false);
holder = new MainHolder();
convertView.setTag(holder);
······
······
}
/*评论界面*/
else
{
ItemHolder holder = null;
convertView = inflater.inflate(R.layout.item, parent, false);
holder = new ItemHolder();
convertView.setTag(holder);
······
······
return convertView;
}
}

Android Resources$NotFoundException: String resource ID #0x1

今天写一个新界面,需要从其他界面通过intent传来参数,就先拿到之后通过TextView的setText显示来看看传参是否正确,写法:TextView.setText(arg) (arg是int型),然后就玩命报错 “android.content.res.Resources$NotFoundException: String resource ID #0x1”,一直不知道怎么回事,看过API才明白当调用setText()方法时如果传入int型是不会被当成内容而是resourceID来使用!R文件里当然不会有我的参数,所以报错!解决方法:TextView.setText(“” + arg)

Android FragmentTransaction 在不同版本下的奇怪问题

目前在做的一款APP,用到了一个布局块中的Fragment的切换,其中一个按钮按下后的事件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
btnTranslate.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
/*当前界面不是翻译界面时才进行下列动作*/
if( MAIN_FRAGMENT_ID != currentFragmentId )
{
setChoosedBtnState(MAIN_FRAGMENT_ID);
FragmentManager fragmentManager = getFragmentManager();
if( null == fragmentTranslate )
fragmentTranslate = new MainUI();
hideCurrentFragment(fragmentTransaction);
fragmentTransaction.show(fragmentTranslate);
fragmentTransaction.add(R.id.fragment_content, fragmentTranslate, "MainUI");
fragmentTransaction.commit();
currentFragmentId = MAIN_FRAGMENT_ID;
}
}
});

fragmentTransaction.commit();currentFragmentId = MAIN_FRAGMENT_ID;}}});
1
2
hideCurrentFragment(fragmentTransaction);
fragmentTransaction.show(fragmentTranslate);

这两句本来是为了提高切换速度,并且保存用户产生的数据而选用隐藏/显示来代替replace(remove/add),运行时在Android 4.0的模拟器和我Android 4.1的手机上没有任何问题,效果也很好,但是当我把程序放在我的Android 4.4(Galaxy S5··貌似这个机型问题挺多···)时出现闪退!具体情形是,当我点击一个按钮创建了一个Fragment的实例时程序正常运行,但是当我再次点击已经创建过实例的界面按钮时就会闪退,一直不明白是怎么回事,查了API也没找到相关的说明。

搞了好久,最后看到

1
<pre name="code" class="java">fragmentTransaction.add(R.id.fragment_content, fragmentTranslate, "MainUI");

这句话,它作用就是把产生的Fragment实例加入到指定的布局块中,再联想我的问题,也就是在加入一次然后再次加入出现了问题,所以试着只在第一次创建Fragment实例时调用这条语句,果然问题解决!

修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
btnTranslate.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
/*当前界面不是翻译界面时才进行下列动作*/
if( MAIN_FRAGMENT_ID != currentFragmentId )
{
setChoosedBtnState(MAIN_FRAGMENT_ID);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if( null == fragmentTranslate )
{
fragmentTranslate = new MainUI();
fragmentTransaction.add(R.id.fragment_content, fragmentTranslate, "MainUI");
}
hideCurrentFragment(fragmentTransaction);
fragmentTransaction.show(fragmentTranslate);
fragmentTransaction.commit();
currentFragmentId = MAIN_FRAGMENT_ID;
}
}
});

Android Error: No resource found ···

关于 android:id=”@+id/” 和 android:id=”@id/” 的问题

本以为自己已经理解了这两个,无非就是第一次创建id时用+id/呗
但是没想到这里创建顺序是严格按照代码顺序来的,如:在RelativeLayout中

1
2
3
4
5
6
7
<RelativeLayout
...

<Widget1 android:layout_above="@id/widget_2" .../>
<Widget2 android:id="@+id/widget_2" ... />

<RelativeLayout/>

这样写会报错,大致说是Widget1找不到widget_2!!!!

原来需要在Widget1中这么写:android:layout_above=”@+id/widget_2″
也就是在第一次遇到这个id时就创建它,放心,这个id还是Widget2的,只是先被Widget1拿去创建并引用了而已

JAVA 设置 JTable 表格的可编辑性

有时候,我们需要设置JTable表格某些行某些列不可编辑以保证数据准确,用DefaultTableModel初始化的话,需要重写它的public boolean isCellEditable(int, int)方法,写法简单呈现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DefaultTableModel myModel = new DefaultTableModel(dataOfOrder, headOfOrder)	//实例化表格模式
{
/**
*
*/
private static final long serialVersionUID = 1L;

public boolean isCellEditable(int rowIndex, int columnIndex) //重写方法改编可编辑性
{
if( columnIndex == getColumnCount() - 1 )
return true;
return false;
}
};

上述写法是设置只有表格的最后一列可编辑,其他列不可编辑。有了rowIndex和columnIndex这两个参数,可以随意的设置可编辑范围。

注意要达到目的,定义另一个类继承DefaultTableModel,之后在类中重写方法是不可行的!

Java 窗体中按钮布局问题

在Java窗体中,有时需要对按钮的设置进行布局,这时使用GridLayout(x, y)会很方便,意思是把窗口划分为x行y列的小格子,在add的时候就是一行一行的填充,这样可以是按钮得到标准化布局

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
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class test_for_blog extends JFrame
{
/**
*
*/
private static final long serialVersionUID = 5136083409273453255L;

private static int JFwidth = 450;
private static int JFheight = 350;

JPanel panel = null;

JButton btnAdd = null;
JButton btnDelete = null;

public test_for_blog()
{
super("按钮添加测试");

panel = new JPanel();

btnAdd = new JButton("Add");
btnDelete = new JButton("Delete");

panel.setLayout( new GridLayout(1, 2) );
panel.add(btnAdd);
panel.add(btnDelete);
this.getContentPane().add(panel, BorderLayout.SOUTH);
}

public static void main(String[] args)
{
test_for_blog test = new test_for_blog();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); //获取显示屏尺寸
test.setBounds((screenSize.width - JFwidth) / 2, (screenSize.height - JFheight) / 2, JFwidth, JFheight); //使程序窗口居中
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //窗口设为可关闭
test.setResizable(false); //窗口设为不可调节大小
test.setVisible(true); //窗口设为可见
}
}