layout/activity_main.xml
<LinearLayout 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"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:text="hello world"
android:textSize="20sp"
android:textColor="@drawable/change_text_color"
android:background="@drawable/change_text_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
LinearLayout>
drawable/change_text_color.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@android:color/white" android:state_selected="true" />
<item android:color="@android:color/black" />
selector>
drawable/change_text_background.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/aaa" android:state_selected="true" />
<item android:drawable="@mipmap/bbb" />
selector>
com/example/pack/MainActivity.java
TextView textView = (TextView) findViewBy(R.id.textView);
textView.setTextAppearance(this, textView.isSelected() ? R.style.text_view_selected : R.style.text_view_not_selected);
values/styles.xml
<resources>
<style name="text_view_selected">
- "android:textSize"
>25sp
- "android:textStyle">bold
style>
<style name="text_view_not_selected">
- "android:textSize"
>23sp
- "android:textStyle">normal
style>
resources>
layout/activity_main.xml
<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"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:text="hello world"
android:textSize="20sp"
android:textColor="@android:color/black"
android:background="@drawable/text_underlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
RelativeLayout>
drawable/text_underlined.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<layer-list>
<item>
<shape android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<corners android:radius="0dp" />
shape>
item>
<item android:gravity="bottom|center" android:left="8dp" android:top="40dp" android:right="8dp">
<shape android:shape="rectangle">
<solid android:color="@android:color/black" />
<corners android:radius="0dp" />
shape>
item>
layer-list>
item>
<item>
<shape android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<corners android:radius="0dp" />
shape>
item>
selector>
layout/activity_main.xml
<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"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:text="hello world"
android:textSize="20sp"
android:textColor="@android:color/black"
android:background="@drawable/change_text_background_press"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
RelativeLayout>
drawable/change_text_background_press.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<layer-list>
<item android:drawable="@mipmap/aaa" />
<item>
<shape android:shape="rectangle">
<solid android:color="#26000000" />
<corners android:radius="0dp" />
shape>
item>
layer-list>
item>
<item>
<item android:drawable="@mipmap/aaa" />
item>
selector>
layout/activity_main.xml
<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"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:text="hello world"
android:textSize="20sp"
android:textColor="@android:color/black"
android:background="@drawable/change_text_background_press"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
RelativeLayout>
drawable/change_text_background_press.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:enabled="false">
<layer-list>
<item android:drawable="@mipmap/aaa" />
<item>
<shape android:shape="rectangle">
<solid android:color="#C2C2C2" />
<corners android:radius="0dp" />
shape>
item>
layer-list>
item>
<item android:state_selected="true">
<layer-list>
<item android:drawable="@mipmap/aaa" />
<item>
<shape android:shape="rectangle">
<solid android:color="#26000000" />
<corners android:radius="0dp" />
shape>
item>
layer-list>
item>
<item>
<item android:drawable="@mipmap/aaa" />
item>
selector>
按照网上的说法,谷歌推荐用newInstance()来创建一个Fragment。
因为当没有强制屏幕的方向的时候,一旦屏幕发生变化,Activity会重新加载,相应地Fragment也会重新加载。
在Fragment重新加载的过程中,如果没有用newInstance()来创建,那么之前传进来的Bundle可能会丢失。
参考:【Android面试】为什么要用newInstance来实例化Fragment
com/example/pack/fragments/TestFragment.java
public class TestFragment extends Fragment {
int arg1;
String arg2;
public static TestFragment newInstance(int param1, String param2) {
TestFragment fragment = new TestFragment();
Bundle data = new Bundle();
data.putInt("param1", param1);
data.putString("param2", param2);
fragment.setArguments(data);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle data = getArguments();
if (data != null) {
arg1 = data.getInt("param1");
arg2 = data.getInt("param2");
}
}
// 如果在新建Fragment的时候选了自动生成回调函数的话
// 那么记得实现它 否则会一直报错
// 如果不想实现它 那么记得要删掉它
}
com/example/pack/adapters/TestFragmentAdapter.java
public class TestFragmentAdapter extends FragmentPagerAdapter {
// 不需要额外写一个Fragment数组来缓存
// 因为原来的FragmentPagerAdapter提供了缓存机制
// 源码里面有一个public Object InstantiateItem(ViewGroup container, int position) {}
// FragmentPagerAdapter不会每一次都调用getItem()
// 因为它调用过一次getItem之后就会缓存fragment 并且给一个id给这个fragment
// 下一次需要调用的时候 先去缓存里面找 如果没有才重新getItem()
// private Fragment[] list;
pubic TestFragmentAdapter(FragmentManager manager) {
super(manager);
}
@Override
public long getItemId(int position) {return position;}
// 这里的返回值只能是Fragment 不能改
// 所以自动转换了类型
// 如果传入一个List list也可以
// 但是千万不要在塞进list的时候强制类型转换 因为会报错
// 直接在newInstance的时候就要转换
@Override
public Fragment getItem(int position) {
switch(position) {
case 0:
return TestFragment.newInstance(0);
case 1:
return TestFragment.newInstance(1);
case 2:
return TestFragment.newInstance(2);
default:
return null;
}
}
@Override
public int getCount() {return COUNT;}
}
com/example/pack/MainActivity.java
TestFragmentAdapter testFragmentAdapter = new TestFragmentAdapter(getFragmentManager());
ViewPager viewPager = (ViewPager) findViewBy(R.id.viewPager);
viewPager.setAdapter(testFragmentAdapter);
如果只是纯粹滚动到某一个页面,而不需要关注像素值、偏移值或者ViewPager的页面状态的话可以直接重写onPageSelected。
com/example/pack/MainActivity.java
TestFragmentAdapter testFragmentAdapter = new TestFragmentAdapter(getFragmentManager());
ViewPager viewPager = (ViewPager) findViewBy(R.id.viewPager);
viewPager.setAdapter(testFragmentAdapter);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageSelected(int position) {
// 当点击到某个按钮时 需要ViewPager跳转到特定页面
// 源码提供的setCurrentItem有两个
// 一个是setCurrentItem(int item) 另一个是setCurrentItem(int item, boolean smoothScroll)
// smoothScroll为真是平滑滑动 而smoothScroll为假就是立刻滑动到特定的页面
// 如果不设置smoothScroll的话 也有平滑滑动 但前提是已经经过了第一层布局
viewPager.setCurrentItem(position, SMOOTH_SCROLL);
}
@Override
public void onPageScrolledStateChanged(int state) {}
});
如果要实现的东西是打竖单排放置的列表,那就直接用ListView。
因为RecyclerView没有分割线,所以如果要添加分割线的话,就得写一个ItemDecoration的类。
如果要取消ListView的分割线,可以用divider="@null
。
如果要添加最上面的,也就是头部的分割线,可以用headerDividersEnabled
,底部的分割线可以用footerDividersEnabled
。也可以在java里面用addFooterView()
<LinearLayout 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"
tools:context=".MainActivity">
<ListView
android:id="@+id/ListView"
android:divider="@null"
android:footerDividersEnabled="true"
android:headerDividersEnabled="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
LinearLayout>
RecyclerView可以实现GridView的功能,还可以实现瀑布流,横向纵向列表,多行的网格。
不管是用RecyclerView实现哪种形式的列表,都要设置layoutManager,否则RecyclerView会加载不出来。
com/example/pack/MainActivity.java
RecyclerView recyclerView = (RecyclerView) findViewBy(R.id.recyclerView);
// 如果要实现GridView 那么就要设置列数
GridLayoutManager gridLayoutManager = new GridLayoutManager(MainActivity.this, COLUMN_NUM);
recyclerView.setLayoutManager(gridLayoutManager);
// 如果要实现横向的ListView 那么就设置方向
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(MainActivity.this, LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(linearLayoutManager);
如果要禁止RecyclerView滚动,可以在layoutManager里面设置,重写canScrollHorizontally和canScrollVertically
RecyclerView recyclerView = (RecyclerView) findViewBy(R.id.recyclerView);
// 如果要实现GridView 那么就要设置列数
GridLayoutManager gridLayoutManager = new GridLayoutManager(MainActivity.this, COLUMN_NUM, false) {
@Override
public boolean canScrollHorizontally() {return false;}
@Override
public boolean canScrollVertically() {return false;}
};
recyclerView.setLayoutManager(gridLayoutManager);
// 如果要实现横向的ListView 那么就设置方向
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(MainActivity.this, LinearLayoutManager.HORIZONTAL, false) {
@Override
public boolean canScrollHorizontally() {return false;}
};
recyclerView.setLayoutManager(linearLayoutManager);
RecyclerView的adapter是用自带的adapter,也就是要提前定义好ViewHolder。这个adapter的ViewHolder其实和BaseAdapter差不多,只不过是把ViewHolder单独拿出来定义好。这个adapter和BaseAdapter最大的不同是把getView拆成了onCreateViewHolder和onBindViewHolder,就不用像BaseAdapter那样在getView里面写一段if (convertView == null)
来分情况了。
placeHolder其实就是把RecyclerView的item所包含的需要改动的所有东西取出来,不需要改动的东西不用取出来,例如如果每一个item都要插入一个一模一样的icon,那这个ImageView就不用取出来。
layout/test_fragment_recycler_view.xml
<LinearLayout 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"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:text="hello world"
android:textSize="20sp"
android:textColor="@drawable/change_text_color"
android:background="@drawable/change_text_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
LinearLayout>
com/example/pack/view/TestFragmentViewHolder.java
public class TestFragmentViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public TestFragmentViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewBy(R.id.textView);
}
}
com/example/pack/adapters/TestFragmentAdapter.java
public class TestFragmentAdapter extends RecyclerView.Adapter<TestFragmentViewHolder> {
private Context mContext;
private List<Integer> mListItems;
private int mLastNum;
public TestFragmentAdapter(Context mContext) {
this.mContext = mContext;
mListItems = new ArrayList<>();
mLastNum = 0;
}
public void setListItems(List<Integer> list) {
if (list == null) return;
if (mListItems == null) mListItems = new ArrayList<>();
else if (!mListItems.isEmpty()) mListItems.clear();
mListItems.addAll(list);
}
@Override
public int getItemCount() {return mListItems.size();}
@Override
public long getItemId(int position) {return mListItems.get(position);}
@Override
public TestFragmentViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.test_fragment_recycler_view, null);
return new TestFragmentViewHolder(view);
}
@Override
public void onBindViewHolder(TestFragmentViewHolder holder, int position) {
// 处理数据
// 各种赋值操作、回调或者别的
// 如果想默认选中第一个位置的话可以用
// 每一次刷新的时候改变lastNum就行了
holder.itemView.setSelected(mLastNum == position);
holder.itemView.setOnClickListener(new View.OnClickListener() {
});
}
public void setLastNum(int lastNum) {
mLastNum = lastNum;
}
}
com/example/pack/MainActivity.java
RecyclerView recyclerView = (RecyclerView) findViewBy(R.id.recyclerView);
TestFragmentAdapter testFragmentAdapter = new TestFragmentAdapter(MainActivity.this);
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(i);
}
testFragmentAdapter.setListItems(list);
recyclerView.setAdapter(testFragmentAdapter);
com/example/pack/MainActivity.java
RecyclerView recyclerView = (RecyclerView) findViewBy(R.id.recyclerView);
// 尽量不用getChildAt,因为findViewHolderForLayoutPosition是实现好的
recyclerView.findViewHolderForLayoutPosition(NUM).itemView.setSelected(SELECTED);
// 滚动到某个位置,也是尽量用现成的,不用重新计算滑动距离
recyclerView.scrollToPosition(POSITION);
直接使用findFragmentByTag很容易出现空对象引用,需要在前面加几行代码
com/example/pack/MainActivity.java
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTranscation();
transaction.replace(R.id.container, new TestFragment(), "testFragment");
// 这句也要写!!!!!!!!!!!!
transaction.addToBackStack("testFragment");
// commit不是马上提交的,是等到后来才把FragmentTransaction提交上去的,存在延迟
// 所以当后面直接取fragment的时候可能取不到
// 一个FragmentTransaction只能提交一个commit,如果有多个任务,要不分开写,要不最后一并提交
transaction.commit();
// 一定要写这一句!!!!!!!!!!!
manager.executePendingTransactions();
TestFragment fragment = (TestFragment) manager.findFragmentByTag("testFragment");
为了让UI能够更好地适配不同分辨率的机器,肯定是用dp。因为dp可以根据不同分辨率,在原有的UI的基础上系统自动对其进行放大缩小。而px则是限定了位置,不会根据分辨率进行变化,很可能出现UI界面混乱的情况。
在java代码中,尤其是跟位置有关的变量,尽量不直接写数据去赋值,而是改用dimens.xml的数据来赋值,因为直接赋值就是px了,不能适配。如果是改变字体的大小的话,setTextSize(int sp)
的输入就是sp,可以直接赋值。
将dp变成px写入代码
values/dimens.xml
<resources>
<dimen name="dp_125">125dpdimen>
resources>
com/example/pack/MainActivity.java
// dp转成像素
int px = getResources().getDimensionPixelSize(R.dimen.dp_125);
// dp转成sp
int px = getResources().getDimensionPixelSize(R.dimen.dp_25);
TextView textView = (TextView) findViewBy(R.id.textView);
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, px);
可以不用ScrollView,因为ScrollView等同于在外面再加一层布局,不好。可以直接用TextView.setMovementMethod(new ScrollingMovementMethod()),就可以让文本内容滚动。
ScrollingMovementMethod继承的是BaseMovementMethod,BaseMovementMethod的源码里给出了向上下左右滚动的算法。
BaseMovementMethod.java
/**
* Performs a scroll left action.
* Scrolls left by the specified number of characters.
*
* @param widget The text view.
* @param buffer The text buffer.
* @param amount The number of characters to scroll by. Must be at least 1.
* @return True if the event was handled.
* @hide
*/
protected boolean scrollLeft(TextView widget, Spannable buffer, int amount) {
final int minScrollX = getScrollBoundsLeft(widget);
int scrollX = widget.getScrollX();
if (scrollX > minScrollX) {
scrollX = Math.max(scrollX - getCharacterWidth(widget) * amount, minScrollX);
widget.scrollTo(scrollX, widget.getScrollY());
return true;
}
return false;
}
/**
* Performs a scroll right action.
* Scrolls right by the specified number of characters.
*
* @param widget The text view.
* @param buffer The text buffer.
* @param amount The number of characters to scroll by. Must be at least 1.
* @return True if the event was handled.
* @hide
*/
protected boolean scrollRight(TextView widget, Spannable buffer, int amount) {
final int maxScrollX = getScrollBoundsRight(widget) - getInnerWidth(widget);
int scrollX = widget.getScrollX();
if (scrollX < maxScrollX) {
scrollX = Math.min(scrollX + getCharacterWidth(widget) * amount, maxScrollX);
widget.scrollTo(scrollX, widget.getScrollY());
return true;
}
return false;
}
/**
* Performs a scroll up action.
* Scrolls up by the specified number of lines.
*
* @param widget The text view.
* @param buffer The text buffer.
* @param amount The number of lines to scroll by. Must be at least 1.
* @return True if the event was handled.
* @hide
*/
protected boolean scrollUp(TextView widget, Spannable buffer, int amount) {
final Layout layout = widget.getLayout();
final int top = widget.getScrollY();
int topLine = layout.getLineForVertical(top);
if (layout.getLineTop(topLine) == top) {
// If the top line is partially visible, bring it all the way
// into view; otherwise, bring the previous line into view.
topLine -= 1;
}
if (topLine >= 0) {
topLine = Math.max(topLine - amount + 1, 0);
Touch.scrollTo(widget, layout, widget.getScrollX(), layout.getLineTop(topLine));
return true;
}
return false;
}
/**
* Performs a scroll down action.
* Scrolls down by the specified number of lines.
*
* @param widget The text view.
* @param buffer The text buffer.
* @param amount The number of lines to scroll by. Must be at least 1.
* @return True if the event was handled.
* @hide
*/
protected boolean scrollDown(TextView widget, Spannable buffer, int amount) {
final Layout layout = widget.getLayout();
final int innerHeight = getInnerHeight(widget);
final int bottom = widget.getScrollY() + innerHeight;
int bottomLine = layout.getLineForVertical(bottom);
if (layout.getLineTop(bottomLine + 1) < bottom + 1) {
// Less than a pixel of this line is out of view,
// so we must have tried to make it entirely in view
// and now want the next line to be in view instead.
bottomLine += 1;
}
final int limit = layout.getLineCount() - 1;
if (bottomLine <= limit) {
bottomLine = Math.min(bottomLine + amount - 1, limit);
Touch.scrollTo(widget, layout, widget.getScrollX(),
layout.getLineTop(bottomLine + 1) - innerHeight);
return true;
}
return false;
}
如果在某种情况下使用系统自带的触摸事件,某种情况下使用自己重写的触摸事件的时候,就要用到super.onTouchEvent(MotionEvent event)了。如果全程都用自己重写的触摸事件的话,那么可以直接屏蔽到系统的触摸事件。
MyTextView.java
// Android Studio建议加上这一行代码
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
// 如果这种情况下需要用到原来的触摸事件
// 那么就直接返回父类实现好的触摸事件
// 返回别的true或者false都是没有用的
// 因为已经把父类的触摸事件屏蔽掉了
if (!mCanTouch) {
return super.onTouchEvent(event);
} else {
// 后面就是实现自己的触摸事件了
// switch (event.getAction()) {
// case ACTION_DOWN:
// XXX;
// break;
//
// case ACTION_MOVE:
// XXX;
// break;
//
// case ACTION_UP:
// XXX;
// break;
// }
// ...
// if (xxx) return true;
// else return false;
// invalidate()
// 如果调用重绘的话,就会自动调用onDraw()
// 如果每一次触摸都调用一次invalidate,那么每一次都会清屏重绘
}
}
用getLayout()获取Layout一般都会存在延时的问题,可以加上一个ViewTreeObserver
MyTextView.java
private Layout mLayout;
private ViewTreeObserver mVto;
mVto = getViewTreeObserver();
mVto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void OnGlobalLayout() {
mLayout = getLayout();
}
});
如果Layout不急着用但是可以确定肯定要执行完的话,甚至可以开一个Runnable去解决
MyTextView.java
// 前面还可以加个计时器
// new CountDownTimer(MILLISECONDS, TIME_INTERVAL) {
// @Override
// public void onTick(long millisUntilFinished) {
// }
//
// @Override
// public void onFinish() {
// Log.d(TAG, "onFinish: finishCountDownTimer!");
// ...
// ...
// }
// }.start();
new Thread(new Runnable() {
@Override
public void run() {
mLayout = getLayout();
while (mLayout == null) {
mLayout = getLayout();
}
// 执行要执行的操作
// ...
// ...
}
}).start();
关于字体的top、ascent、baseline…
参考:Android font, 字体全攻略;
虽然算出了字符的坐标位置,但是在后续使用的时候还要考虑到paddingTop、paddingBottom、paddingLeft、paddingRight和scrollX、scrollY。因为前面也说了会使用到setMovementMethod,一旦滚动,位置就会发生变化。
MyTextView.java
// 获得离(x, y)最近的字符是第几个
int currPos = getOffsetForPosition(x, y);
Layout layout = getLayout();
// 给一整行的文字画一个矩形框住
Rect bound = new Rect();
// 当前是第几行
int currLine = layout.getLineForOffset(position);
// 获取矩形的边界
// 矩形的上边界就是字体的上边
// 矩形的下边界就是字体的下边
layout.getLineBounds(currLine, bound);
int top = bound.top;
int bottom = bound.bottom;
// 字符的左手边的坐标
int left = layout.getPrimaryHorizontal(position);
除了获取子串substring这种常规的操作以外,去除字符串头尾空格可以用现成的trim()。
MyTextView.java
String str = " xxxx ";
str.trim();
如果是要更改TextView某一部分子串的样式,那么就可以用上SpannableString。
参考:安卓开发中SpannableString之富文本显示效果
com/example/pack/MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 就是这两行
getSupportActionBar().hide();
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
}
com/example/pack/MainActivity.java
InputMethodManger manager = (InputMethodManger) MainActivity.this.getSystemService(Context.INPUT_METHOD_SERVICE);
// 一定要判断空不空
// 两个都要判断
// 不然很容易出错
if (manager != null && MainActivity.this.getCurrentFocus() != null) manager.hideSoftInputFromWindow(MainActivity.this.getCurrentFocus().getWindowToken(), InputMethodManger.HIDE_NOT_ALWAYS);
其中一个方法可以用setResult(),也可以用广播,不过广播麻烦一点。
com/example/pack/fragments/TestFragment.java
private void inflateActivity() {
Bundle data = new Bundle();
data.putSerializable("dataset", dataset);
Intent intent = new Intent(getActivity(), SecondActity.class);
intent.putExtras(data);
// 因为要接收SecondActivity传回来的数据
// 所以要发送一个请求码
// 是用来识别传回来的数据是不是属于要接收的那个Activity的数据
startActivityForResult(intent, 0x11);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 0x11) {
if (resultCode == RESULT_OK) {
String returnData = data.getStringExtra("dataString");
// 拿到数据的后续处理
// ...
// ...
}
}
}
com/example/pack/SecondActivity.java
// 跳转到SecondActiv的时候拿到数据
private void initData() {
Intent intent = getIntent();
Bundle data = intent.getExtras();
Dataset dataset = data.getSerializable("dataset");
}
// 退出的时候要返回数据给Fragment
private void handleReturn() {
Intent intent = new Intent();
// 要传回的数据
intent.putExtra("dataString", dataString);
// 要传回结果
setResult(RESULT_OK, intent);
SecondActivity.this.finish();
}
如果只是普通的DialogFragment的话,可以直接在onCreateView里面设置布局。如果是比较复杂,且有特定的设计的弹窗,那么可以用onCreateDialog()。
com/example/pack/fragments/MyDialogFragment.java
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 一定要在onCreate里面写!!!!!!!!
// 不要在onCreateView里面写
// 因为没有效果
setStyle(DialogFragment.STYLE_NO_FRAME, R.style.FullScreen);
}
DialogFragment在布局里面设置父布局的宽高是没有用的,不管是具体数据还是match_parent、wrap_content这一类的参数。
如果要规定某一个具体的宽高,那么可以在代码里用setLayout()
。
com/example/pack/fragments/MyDialogFragment.java
private View mView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_my_dialog, container, false);
// 就是这一句
getDialog().getWindow().setLayout(LAYOUT_WIDTH, LAYOUT_HEIGHT);
return mView;
}
如果只是想设置match_parent和wrap_content的话,那么可以设置LayoutParams。需要注意的点是,布局里面不要用
,否则很容易失效,改用
。
com/example/pack/fragments/MyDialogFragment.java
private View mView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_my_dialog, container, false);
WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
params.gravity = Gravity.CENTER;
params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
// 一定要写这一句
// 否则弹窗有可能显示不出来或者显示出来只有很小一块
// PopupWindow也是一个道理 也是要设置backgroundDrawable
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
getDialog().getWindow().setAttributes(params);
return mView;
}
可以用addTextChangedListener()
监听,可以动态计算当前输入的字数,动态获取当前输入的内容。
如果要实现TextView里面最多输入指定数目的文字,而且字数满了之后要提示的话,可以在xml里用maxLength
,同时在java里用addTextChangedListener()
。如果要限制TextView里面输入的最大行数,可以用maxLines
。
layout/activity_main.xml
<LinearLayout 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"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:text="hello world"
android:maxLength="31"
android:maxLines="2"
android:textSize="20sp"
android:textColor="@drawable/change_text_color"
android:background="@drawable/change_text_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
LinearLayout>
com/example/pack/MainActivity.java
TextView textView = (TextView) findViewBy(R.id.textView);
textView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// count就是当前输入字数
// 如果检测到某一个特定的数字就要显示弹窗
if (s.length() == MAX_LENGTH) Toast.makeText(MainActivity.this, "文字已输满!", Toast.LENGTH_SHORT).show();
}
});
走马灯效果里面自动包含了省略号效果,当TextView获取到焦点的时候,文字会滚动;当没有获取到焦点的时候,例如突然弹出了PopupWindow,文字不会滚动,并且超出部分会以省略号...
显示。
layout/activity_main.xml
<LinearLayout 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"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:text="hello world"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:textSize="20sp"
android:textColor="@drawable/change_text_color"
android:background="@drawable/change_text_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
LinearLayout>
如果是省略号效果,可以用ellipsize="end"
,加上singleLine
就是显示一行,如果要限制一行要显示的字数,可以用maxEms
。那么在maxEms
规定的字数到了以后,后面的内容以省略号显示。
<LinearLayout 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"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:text="hello world"
android:ellipsize="end"
android:singleLine="true"
android:maxEms="8"
android:textSize="20sp"
android:textColor="@drawable/change_text_color"
android:background="@drawable/change_text_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
LinearLayout>
有时候输入setText以后,光标还是停在最开始的位置,这样不行,可以用setSelection()
。
com/example/pack/MainActivity.java
TextView TextView = (TextView) findViewBy(R.id.autoCompleteTextView);
// 设置光标的位置在字符串末尾
TextView.setSelection(TextView.getText().toString().length());
改变光标的样式,例如换成一张图片,可以在xml里用textCursorDrawable
。
<LinearLayout 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"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:text="hello world"
android:cursorVisible="true"
android:texctCursorDrawable="@mipmap/cursor"
android:textSize="20sp"
android:textColor="@drawable/change_text_color"
android:background="@drawable/change_text_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
LinearLayout>
可以预先在xml里面定义好一个
,然后往里面加TextView。
com/example/pack/MainActivity.java
LinearLayout layout = (LinearLayout) findViewBy(R.id.layout);
// 把里面有的子组件都清空
layout.removeAllViews();
// 设置位置
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.CENTER_VERTICAL;
// 尽量不要直接写数据 而是获取dimens.xml里的dp
lp.setMargins(getResources().getDimensionPixelSize(R.dimen.dp_25), 0, 0, 0);
TextView textView = new TextView(MainActivity.this);
textView.setLayoutParams(lp);
textView.setTextSize(22);
// 因为那是id值
// 不是颜色对应的那个int值
// 如果直接在setTextColor里面写R.drawable.change_font_color不会报错
// 不过它只能获取到第一种颜色
ColorStateList colorStateList = getResources().getColorStateList(R.drawable.change_font_color);
textView.setTextColor(colorStateList);
// 如果要给textView的内容添加富文本效果
// 可以用SpannableString
SpannableString content = new SpannableString(CONTENT);
content.setSpan(new UnderlineSpan(), 0, LENGTH, 0);
textView.setText(content);
// 最后记得把建好的textView加入到预先创建好的LinearLayout里面
layout.addView(textView);
如果在输入内容过程中,文本框可以根据输入的内容自动下拉提示框,那么可以用AutoCompleteTextView并且绑定一个数组。
AutoCompleteTextView和EditText的用法类似,除了多几个属性dropDownHeight
、dropDownVerticalOffset
……
可以用Arrays
加上ArrayAdapter
来绑定
参考:Android使用xml文件中的array资源
com/example/pack/MainActivity.java
AutoCompleteTextView autoCompleteTextView = (AutoCompleteTextView) findViewBy(R.id.autoCompleteTextView);
int[] arr = getResources().getArray(R.array.language);
ArrayAdapter adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_dropdown_item_line, arr);
autoCompleteTextView.setAdapter(adapter);
如果要给AutoCompleteTextView的下拉列表设置点击事件,可以用setOnItemClickListener()
,用法跟其他View的组件一样。
如果要读入大量数据的话,不能直接在UI线程里直接读取,否则会阻塞界面,可能会出现界面加载超时,activity可能会自动取消。这个时候需要用到子线程,当子线程处理完数据之后向Handler
发送消息,由Handler
控制下一步走到哪里。
如果数据特别多,处理起来很慢,那么可以考虑分部分加载。分部分可以考虑按时间间隔来加载,也可以按照一段一段数据来加载。
如果要用new Thread()
的话,最好是在确保这个Thread
一定会完成的情况下用,因为Thread.interrupt()
和Thread.join()
并不是终止线程,而是暂时打断线程,而且并不知道打断完之后,线程运行到哪一个位置。
因为有用上Handler
,所以Destroy
的时候也要把Handler
移除。
com/example/pack/MainActivity.java
private Handler mHandler = new Handler() {
@Override
public void dispatchMessage(Message msg) {
switch (msg.what) {
case 0x11:
// ...
break;
case 0x12:
// ...
break;
default:
break;
}
}
};
@Override
protected void onDestroy() {
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
}
线程具体的用法如下:
参考:Android线程管理之ExecutorService线程池
参考:Android: How do I stop Runnable?
参考:How to cancel all the thread/ threads in ExcecutorService?
如果使用到多线程的话,记得在某些函数上加锁synchronized
,否则会出现多个线程同时进入一个函数的情况,导致结果出错。
如果只是单纯想要动画效果,而不需要对正在运动的组件加上监听器之类的操作的话,可以直接用逐帧动画或者补间动画。但是如果要对组件进行操作的话,最好使用属性动画。因为前两个在组件运动后,不会更新组件的位置,也就是说如果一个button从a点运动到b点,虽然在屏幕上看到button在b点上,但是如果此时点击b点,button不会有反应,因为监听器绑定的位置还是在a点。
参考:Android动画之ObjectAnimator
参考:Android中的 ObjectAnimator 动画
参考:< Android 基础(三十一)> ObjectAnimator
参考:Android 属性动画(Property Animation) 使用详解
参考:Android动画之ObjectAnimator实现补间动画和ObjectAnimator自定义属性
参考:Android开发中属性动画(ObjectAnimator)中 插值器(Time Interpolator )详解