編輯:關於Android編程
太久沒寫博客了,主要是想寫一些有質量的,今天碰到這個需求覺得挺有意思的,即像標題寫的那樣,這裡記錄一下,廢話不多說,先上一個效果圖:
單行顯示:
大於一行顯示:
即,單行上下滾動,大於一行則實現跑馬燈。
這裡有兩個難點:
· 如何判斷字幕是超過了一行,這個受字體大小和字節編碼影響的
· 如何根據超過了多少定制不同的滾動時間
首先還是先實現上下滾動的效果,從最基礎的步驟來
我們需要上下滾動的效果,所以需要重新 TextSwitcher 的構造方法,這個跟ImageSwitcher 一個樣,差別在 ImageSwitcher 的 makeview 返回的是Imageview,而 TextSwitcher 的makeview 返回的 TextView 。首先,先實現上下滾動:
public class TextSwitcherView extends TextSwitcher implements ViewFactory {
private ArrayList reArrayList = new ArrayList();
private int resIndex = 0;
private final int UPDATE_TEXTSWITCHER = 1;
private int timerStartAgainCount = 0;
private Context mContext;
public TextSwitcherView(Context context) {
super(context);
// TODO Auto-generated constructor stub
mContext = context;
init();
}
public TextSwitcherView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
// TODO Auto-generated constructor stub
}
private void init(){
this.setFactory(this);
this.setInAnimation(getContext(),R.anim.vertical_in);
this.setOutAnimation(getContext(), R.anim.vertical_out);
Timer timer = new Timer();
timer.schedule(timerTask, 1,3000);
}
TimerTask timerTask = new TimerTask() {
@Override
public void run() { //不能在這裡創建任何UI的更新,toast也不行
// TODO Auto-generated method stub
Message msg = new Message();
msg.what = UPDATE_TEXTSWITCHER;
handler.sendMessage(msg);
}
};
Handler handler = new Handler(){
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXTSWITCHER:
updateTextSwitcher();
break;
default:
break;
}
};
};
/**
* 需要傳遞的資源
* @param reArrayList
*/
public void getResource(ArrayList reArrayList) {
this.reArrayList = reArrayList;
}
public void updateTextSwitcher() {
if (this.reArrayList != null && this.reArrayList.size()>0) {
this.setText(this.reArrayList.get(resIndex++));
if (resIndex > this.reArrayList.size()-1) {
resIndex = 0;
}
}
}
@Override
public View makeView() {
// TODO Auto-generated method stub
TextView tView = new TextView(getContext());
tView.setTextSize(20);
tView.setTextColor(0xff24aaff);
return tView;
}
}
數據的獲取是通過一個 ArrayList 來獲取的,然後用一個定時器Timer實現上下滾動,3s執行一次。現在的 makeview 返回的是一個TextView ,所以現在只是上下滾動。
上下滾動動畫代碼如下:
vertical_in.xml:
vertical_out.xml:
我們知道,跑馬燈的效果是focusable = true,即要焦點放在它身上,所以,這裡我們繼承 TextView,重新 isFocus 方法即可,即可實現跑馬燈的效果,如下:
public class WaterTextView extends TextView {
public WaterTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public WaterTextView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public WaterTextView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
@Override
@ExportedProperty(category = "focus")
public boolean isFocused() {
// TODO Auto-generated method stub
return true;
}
}
既然要實現跑馬燈,所以,要在 makeView 返回中,修改返回的是我們的重新的TextView 即可。如:
public View makeView() {
// TODO Auto-generated method stub
WaterTextView tView = new WaterTextView(getContext());
tView.setSingleLine(true);
tView.setEllipsize(TruncateAt.MARQUEE);
tView.setPadding(getResources().getDimensionPixelOffset(R.dimen.x10),
getResources().getDimensionPixelOffset(R.dimen.x5),
getResources().getDimensionPixelOffset(R.dimen.x10),
getResources().getDimensionPixelOffset(R.dimen.x5));
tView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelOffset(R.dimen.x28));
tView.setMarqueeRepeatLimit(1);
Log.d("zsr", "more line");
return tView;
}
這裡設置了一些跑馬燈的屬性,注意字體那裡,有兩個參數,第一個傳入的是像素的標志位,因為 setTextSize 的默認參數為 sp,如果後面傳入的是像素,是要換算的,所以這裡,我們統一用像素,如果你對自適應不了解的話,可以看我以前寫的博客:
http://blog.csdn.net/u011418943/article/details/51247828
你現在可以傳入一些比較長的參數,看看是不是當字數超過一行地時候,有流水燈的效果。
在這裡糾結了很多時間,剛開始的時候考慮像素什麼問題,但獲取的數據都不對,這時我的想法是這樣的,既然規定了字體大小,那麼字體的字節大小就可以確定的,再根據計算一個字節占了屏幕多少,就可以計算一個一行占了多少字節了。
雖然這種方法會存在一些問題,但目前我在公司的板子上都沒出現過。獲取字節,中文一般占兩個字節,英語占一個字節。
下面是我的獲取字節的方法:
public void subStr(String str, int subSLength)
throws UnsupportedEncodingException{
char[] array = str.toCharArray(); //獲取字節
Log.d("zsr", "strbyte/arraybyte: "+str.toString().getBytes("GBK").length+" "+array.length);
if (str.toString().getBytes("GBK").length > subSLength) {
int shi = str.toString().getBytes("GBK").length/subSLength;
int ge = str.toString().getBytes("GBK").length%subSLength;
if (shi > 0 && ge != 0) { //不小於一行,分開顯示
for (int i = 0; i < array.length; i++) {
if((char)(byte)array[i]!=array[i]){
byteCount += 2; //如果是中文,則自加2
stringCount += 1;
}else{
byteCount += 1; //如果不是中文,則自加1
stringCount += 1;
}
if (byteCount >= subSLength) {
getRealCount = stringCount;
byteCount = 0;
stringCount = 0;
}
}
}else {
subArrayList.add(str); //小於一行則正常顯示
Log.d("zsr", "cannot read?");
}
}
我的一行是 48 ,即subSLength = 48 ,既然我們已經成功截取了一行,這裡就有兩種方法了。
· 不采用跑馬燈,采用分行,即截斷分行一行一行顯示
· 繼續采用跑馬燈,等檢測到大於一行,讓上下滾動效果消失,滾動完再上下滾動
public void subStr(String str, int subSLength)
throws UnsupportedEncodingException{
char[] array = str.toCharArray(); //獲取字節
Log.d("zsr", "strbyte/arraybyte: "+str.toString().getBytes("GBK").length+" "+array.length);
if (str.toString().getBytes("GBK").length > subSLength) {
int shi = str.toString().getBytes("GBK").length/subSLength;
int ge = str.toString().getBytes("GBK").length%subSLength;
if (shi > 0 && ge != 0) { //不小於一行,分開顯示
for (int i = 0; i < array.length; i++) {
if((char)(byte)array[i]!=array[i]){
byteCount += 2; //如果是中文,則自加2
stringCount += 1;
}else{
byteCount += 1; //如果不是中文,則自加1
stringCount += 1;
}
if (byteCount >= 48) {
getRealCount = stringCount;
byteCount = 0;
stringCount = 0;
}
}
Log.d("zsr_count", "waterloopTime: "+waterloopTime);
for (int i = 0; i <= shi; i++) { //把超過一行地數據存到一個數組
if (i == shi) {
subArrayList.add(str.substring(getRealCount*i,str.length()));
}else {
subArrayList.add(str.substring(getRealCount*i,getRealCount*(i+1)));
}
}
}
}else {
subArrayList.add(str); //小於一行則正常顯示
Log.d("zsr", "cannot read?");
}
}
這裡的思路是,把多行的截取成一段段存到一個數組裡,注意這裡的字節和數組的元素是不一樣的,中文英語都是一個,所以,我才在上面寫上StringCount。那麼更新這裡,就寫上:
/**
* 需要傳遞的資源
* @param reArrayList
*/
public void setArrayListData(ArrayList reArrayList) {
this.reArrayList = reArrayList;
this.dataflag = 1;
}
public void setDefaultData(String string) {
this.string = string;
this.dataflag = 0;
}
數據處理:
public void updateTextSwitcher() {
if (dataflag == 1) { //如果有數據,顯示數據
if (this.reArrayList != null && this.reArrayList.size()>0) {
try {
//this.setText(subStr(reArrayList.get(resIndex++),48));
if (!subArrayFlag) {
subStr(reArrayList.get(resIndex++), 48);
if (subArrayList != null && subArrayList.size()>0) {
Log.d("zsr", "size: "+subArrayList.size());
if (subArrayList.size() == 1) { //單行
this.setText(subArrayList.get(subIndex));
subArrayList.clear();
}else if(subArrayList.size() > 1) {
this.setText(subArrayList.get(subIndex));
subArrayFlag = true;
}
}
}else {
subIndex++;
if (subArrayList != null && subArrayList.size()>0) {
if (subIndex == subArrayList.size()-1) {
this.setText(subArrayList.get(subIndex));
subArrayFlag = false;
subArrayList.clear();
subIndex = 0;
}
}
}
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (resIndex >=reArrayList.size()) {
resIndex = 0;
}
}
}else { //沒有
this.setText(this.string);
}
}
怎麼樣取消上下滾動,讓左右滾動呢?因為我們上面的是一個定時器Timer的,所以,只要改變參數就行了,為了方便大家看,我們重新寫一個更新的方法,如:
private void updateText(){
if (this.dataflag == 1) {
if (this.reArrayList != null && this.reArrayList.size()>0) {
try {
subStr(reArrayList.get(resIndex), 48);
Log.d("zsr", "size: "+subArrayList.size());
if (subArrayList != null && subArrayList.size()>0) {
if (subArrayList.size() == 1){ // 單行
waterTextStatus = false;
this.setText(this.reArrayList.get(resIndex));
subArrayList.clear();
Log.d("zsr", "ONE");
}else if(subArrayList.size()>1){ // 多行
waterTextStatus = true;
this.setText(this.reArrayList.get(resIndex));
Log.d("zsr", "MORE");
subArrayList.clear();
}
}
resIndex++;
if (resIndex >= reArrayList.size()-1) {
resIndex = 0;
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}else {
this.setText(this.string);
}
}
其實跟上面沒啥區別。注意!這裡,我用了waterTextStatus 來區別是不是大於一行。然後接著就是改變變量了
class MyTimerTask extends TimerTask {
@Override
public void run() {
// TODO Auto-generated method stub
if (!waterTextStatus) {
msg = new Message();
msg.what = UPDATE_TEXTSWITCHER;
handler.sendMessage(msg);
}else { //多行
startAgainCount ++;
if (startAgainCount > waterloopTime) {
startAgainCount = 0;
waterTextStatus = false;
}
}
}
};
線程的更新也很簡單:
Handler handler = new Handler(){
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXTSWITCHER:
//updateTextSwitcher();
//index = next();
updateText();
break;
default:
break;
}
};
};
可以看到,如果多行,就不然它去刷新線程的UI更新,等它跑完了再去更新,至於怎麼判斷是否跑完呢?這裡我也不知道怎麼弄,這能用笨方法,就是延時,尴尬尴尬:
public void subStr(String str, int subSLength)
throws UnsupportedEncodingException{
char[] array = str.toCharArray(); //獲取字節
Log.d("zsr", "strbyte/arraybyte: "+str.toString().getBytes("GBK").length+" "+array.length);
if (str.toString().getBytes("GBK").length > subSLength) {
int shi = str.toString().getBytes("GBK").length/subSLength;
int ge = str.toString().getBytes("GBK").length%subSLength;
if (shi > 0 && ge != 0) { //不小於一行,分開顯示
for (int i = 0; i < array.length; i++) {
if((char)(byte)array[i]!=array[i]){
byteCount += 2; //如果是中文,則自加2
stringCount += 1;
}else{
byteCount += 1; //如果不是中文,則自加1
stringCount += 1;
}
if (byteCount >= 48) {
getRealCount = stringCount;
byteCount = 0;
stringCount = 0;
}
}
Log.d("zsr_count", "shi/ge: "+shi+" "+ge);
if (ge>0 && ge<=7) {
waterloopTime = 3*shi;
}else if (ge>7 && ge<=16) {
waterloopTime = 3*shi+1;
}else if(ge>16 && ge<=25){
waterloopTime = 4*shi+1;
}else if(ge>25 && ge<=35){
waterloopTime = 5*shi+1;
}else {
waterloopTime = 6*shi+2;
}
Log.d("zsr_count", "waterloopTime: "+waterloopTime);
for (int i = 0; i <= shi; i++) {
if (i == shi) {
subArrayList.add(str.substring(getRealCount*i,str.length()));
}else {
subArrayList.add(str.substring(getRealCount*i,getRealCount*(i+1)));
}
}
}
}else {
subArrayList.add(str); //小於一行則正常顯示
Log.d("zsr", "cannot read?");
}
}
可以看到,這裡的waterloopTime 是根據截取的行數和個數來計算的,當然,我現在心裡一直有點忐忑,延時能不用就不要用的,如果你有好建議,也請告訴,謝謝。
布局很簡單:
Service是在一段不定的時間運行在後台,不和用戶交互應用組件。每個Service必須在manifest中 通過<service>來聲明。可以通過conte
在平常的開發中,我們經常會遇到點擊,滑動之類的事件。有時候不同的view之間也存在各種滑動沖突。比如布局的內外兩層都能滑動的話,那麼就會出現沖突了。這個時候我們就需要了解
用的太多了,但是不知道原理。Xutils裡面捨棄了findViewById改用注解,當時也很不理解。一步步了解後,發現,相比注解的方式加載控件,findViewById的
什麼是RecyclerView關於RecyclerView,是一個主要用於展示和回收View的有一個控件,在官用了一句話來概括RecyclerView 是一種通過提供有限