android入门学习
第四章
简单碎片使用
碎片Fragment兼顾平板
嵌入在活动中的UI片段
例子:一个活动中添加两个碎片
新建一个左侧碎片布局和右侧碎片布局
left_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button"
android:layout_gravity="center_horizontal"
android:text="button"/>
</LinearLayout>
right_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#00ffbb"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text="This is a fragment"/>
</LinearLayout>
新建leftFragment和rightFragment类
public class LeftFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.left_fragment, container, false);
return view;
}
}
public class RightFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.right_fragment, container, false);
return view;
}
}
inflater.inflate()
在主活动的界面中添加两个碎片
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="org.nuaa.fragmenttest.LeftFragment"
android:id="@+id/left_fragment"/>
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="org.nuaa.fragmenttest.RightFragment"
android:id="@+id/right_fragment"/>
</LinearLayout>
使用name指定要添加的碎片类名
动态添加碎片
新建一个another_right_fragment.xml,内容与right_fragment.xml类似
AnotherRightFragment类也是与RightFragment类似,也是重写onCreateView
修改主活动的布局,将右边改成FragmentLayout
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/right_layout"/>
修改主活动
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
replaceFragment(new RightFragment());
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
replaceFragment(new AnotherRightFragment());
break;
default: break;
}
}
private void replaceFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.commit();
}
}
新建repalceFragment方法,修改FragmentLayout中的碎片
初始化replaceFragment(new RightFragment()),点击按钮后repalceFragment(new AnotherRightFragent())替换
动态添加碎片的步骤:
- 创建待添加的碎片实例replaceFragment(new XXXXX())
- 获取FragmentManager,在活动中可以直接通过调用getSupportFragmentManager()方法获取
- 开启一个事务,通过调用beginTransaction()方法开启
- 向容器内添加或替换碎片,使用replace(),传入容器的id和代添加的碎片实例
- 提交事务,commit()
在碎片中模拟返回栈
在commit()之前添加一句transaction.addToBackStack(null)
,参数是String名字,用于描述返回栈的状态。
碎片和活动之间通信
活动中调用碎片
RightFragment rightFragment = (RightFragment) getSupportFragmentManager().findFragmentById(R.id.right_fragment);
碎片中调用活动
MainActivity activity = (MainActivity) getActivity();
有活动实例后,调用活动中的方法就容易了。另外当碎片中使用Context对象时,也可以用getActivity(),因为活动本身就是一个Context对象
碎片生命周期
运行,暂停,停止,销毁
- 运行
碎片可见,关联的活动处于运行状态,碎片也处于运行 - 暂停
活动进入暂停状态时(由于另外一个未占满屏幕的活动被添加到栈顶),相关联的碎片就会进入暂停 - 停止
当活动进入暂停状态,关联的碎片就会进入停止状态,或,
通过调用FragmentManager.remove()、repalce()方法将碎片从活动中移除,如果事务在提交前调用addToBackStack(),碎片也会进入停止状态,对用户完全不可见,有可能被系统回收 - 销毁
碎片总是依附于活动,活动销毁,碎片也会销毁,或,
通过调用FragmentManager.remove()、repalce()方法将碎片从活动中移除,事务在提交前没有调用addToBackStack(),碎片会进入销毁状态
Fragment类的回调方法
一般的活动有的方法,碎片都有,还有一些附加的
- onAttach():当碎片和活动建立关联时
- onCreateView():碎片创建视图,加载布局时调用
- onActivityCreate():确保与碎片关联的活动一定已经创建完毕的时候调用
- onDestroyView():与碎片关联的试图被移除的时候调用
- onDetach():碎片与活动解除关联时使用
生命周期
添加一个碎片
|
onAttach
|
onCreat
|
onCreateView
|
onActivityCreate
|
onStart
|
onResume
|
碎片激活
|
onPause
|
onStop
|
onDestroyView
|
onDestroy
|
onDetach
|
碎片已销毁
动态加载布局的技巧
使用限定符
判断时是使用单页还是双页模式,借助限定符Qualifiers
屏幕大小 | 描述 |
---|---|
small | 小屏幕 |
normal | 中等屏幕 |
large | 大屏幕 |
xlarge | 超大屏幕 |
分辨率 | 描述 |
---|---|
ldpi | 低分辨率120dpi以下 |
mdpi | 中等分辨率120dpi - 160dpi |
hdpi | 高分辨率160dpi - 240dpi |
xhdpi | 超高分辨率240dpi - 320dpi |
xxhdpi | 超超高分辨率320dpi - 480dpi |
方向 | 描述 |
---|---|
land | 横屏 |
port | 竖屏 |
最小宽度限定符
手动选择布局
最小宽度限定符运行对屏幕的宽度指定一个最小值,以dp为单位,作为临界点
例如新建一个layout-sw600dp的文件夹,说明在屏幕宽度大于600dp的设备上,会加载该文件夹下的文件,否则加载默认的layout下的布局文件
碎片实践 - 新闻应用
新建新闻类News
public class News {
private String title;
private String content;
public String getTitle() {
return title;
}
public String getContent() {
return content;
}
public void setTitle(String title) {
this.title = title;
}
public void setContent(String content) {
this.content = content;
}
}
News中,title标题,content内容
新建布局文件news_content_frag.xml,作为新闻内容布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="invisible"
android:id="@+id/visibility_layout">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:textSize="25sp"
android:id="@+id/news_title"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000"/>
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="15dp"
android:textSize="18sp"
android:id="@+id/news_content"/>
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="#000"/>
</RelativeLayout>
黑色横线是用View实现的,颜色设置为黑色,宽或高设置为1dp
新建一个NewsContentFragment类,继承自Fragment
public class NewsContentFragment extends Fragment {
private View view;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.news_content_frag, container, false);
return view;
}
public void refresh(String newsTitle, String newsContent) {
View visibilityLayout = view.findViewById(R.id.visibility_layout);
visibilityLayout.setVisibility(View.VISIBLE);
TextView newsTitleText = (TextView) view.findViewById(R.id.news_title);
TextView newsContentText = (TextView) view.findViewById(R.id.news_content);
newsTitleText.setText(newsTitle);
newsContentText.setText(newsContent);
}
}
加载的是news_content_frag.xml布局,refresh方法是每次点击后将新闻的标题和内容显示在fragment中。
以上都是双页模式中使用的内容
创建一个新的活动NewsContentActivity,布局名为news_content.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="org.nuaa.fragmentbestpractice.NewsContentFragment"
android:id="@+id/news_content_fragment"/>
</LinearLayout>
这里直接复用NewsContentFragment
修改NewsContentActivity代码
public class NewsContentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_content);
String newsTitle = getIntent().getStringExtra("news_title");
String newsContent = getIntent().getStringExtra("news_content");
NewsContentFragment newsContentFragment = (NewsContentFragment)
getSupportFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(newsTitle, newsContent);
}
public static void actionStart(Context context, String newsTitle, String newsContent) {
Intent intent = new Intent(context, NewsContentActivity.class);
intent.putExtra("news_title", newsTitle);
intent.putExtra("news_content", newsContent);
context.startActivity(intent);
}
}
这是在小屏手机应用的活动,actionStart是static方法,用于启动活动。onCreate中初始化fragment,使用getSupportFragmentManager获取fragment,再调用refrash。
创建用于显示新闻列表的布局news_title_frag.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/news_title_recycler_view"/>
</LinearLayout>
还有子项布局news_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/news_title"
android:maxLines="1"
android:ellipsize="end"
android:textSize="18sp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="15dp"
android:paddingBottom="15dp" />
ellipsize是设定当文本内容超出宽度时,文本缩略方式
新建NewsTitleFragment类
public class NewsTitleFragment extends Fragment {
private boolean isTwoPane;
class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {
private List<News> mNewsList;
class ViewHolder extends RecyclerView.ViewHolder {
TextView newsTitleText;
public ViewHolder(View view) {
super(view);
newsTitleText = (TextView) view.findViewById(R.id.news_title);
}
}
public NewsAdapter(List<News> newsList) {
mNewsList = newsList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item, parent, false);
final ViewHolder holder = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
News news = mNewsList.get(holder.getAdapterPosition());
if (isTwoPane) {
NewsContentFragment newsContentFragment = (NewsContentFragment)
getFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(), news.getContent());
} else {
NewsContentActivity.actionStart(getActivity(), news.getTitle(), news.getContent());
}
}
});
return holder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
News news = mNewsList.get(position);
holder.newsTitleText.setText(news.getTitle());
}
@Override
public int getItemCount() {
return mNewsList.size();
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.news_title_frag, container ,false);
RecyclerView newsTitleRecyclerView = (RecyclerView) view.findViewById(R.id.news_title_recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
newsTitleRecyclerView.setLayoutManager(layoutManager);
NewsAdapter adapter = new NewsAdapter(getNews());
newsTitleRecyclerView.setAdapter(adapter);
return view;
}
private List<News> getNews() {
List<News> newsList = new ArrayList<>();
for (int i = 1; i <= 50; i++) {
News news = new News();
news.setTitle("This is news title" + i);
news.setContent(getRandomLengthContent("This is news content " + i + "."));
newsList.add(news);
}
return newsList;
}
private String getRandomLengthContent(String content) {
Random random = new Random();
int length = random.nextInt(20) + 1;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(content);
}
return builder.toString();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity().findViewById(R.id.news_content_layout) != null) {
isTwoPane = true;
} else {
isTwoPane = false;
}
}
}
修改layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/news_title_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="org.nuaa.fragmentbestpractice.NewsTitleFragment"
android:id="@+id/news_title_fragment"/>
</FrameLayout>
再新建layout_large/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="org.nuaa.fragmentbestpractice.NewsTitleFragment"
android:id="@+id/news_title_fragment"/>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3"
android:id="@+id/news_content_layout">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="org.nuaa.fragmentbestpractice.NewsContentFragment"
android:id="@+id/news_content_fragment"/>
</FrameLayout>
</LinearLayout>
在onActivityCreated方法中,使用if(getActivity().findViewById(R.id.news_content.layout) != null)
即可判断是双页还是单页
第五章
广播机制简介
Android有一套完整的API,可以*发送和接收广播
发送使用Intent,接收使用接收器Broadcast Receiver
广播有两种类型:标准广播和有序广播
- 标准广播:完全异步执行,广播发出后,所有接收器几乎会在同一时刻收到,没有先后顺序,无法截断
- 有序广播:同步执行,广播发出后,同一时间只有一个能接收到,当该广播接收器中的逻辑执行完毕,才会继续传递,有先后顺序,优先级高的先收到,还可以截断
接收系统广播
注册的方式有两种:
- 动态注册:在代码中注册
- 静态注册:AndroidManifest.xml注册
动态注册监听网络变化
广播接收器,继承Broadcast类,并重写onReceive方法,具体接收到广播后的处理逻辑就写在里面
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private NetworkChangeReceiver networkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver, intentFilter);
}
class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("TAG", "changed");
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
Network network = connectivityManager.getActiveNetwork();
if (network == null) {
Toast.makeText(context, "network is unavailable", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "network is available", Toast.LENGTH_LONG).show();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(networkChangeReceiver);
}
}
创建NetworkChangeReceiver类
在onCreate中新建IntentFilter实例,添加一个值android.net.conn.CONNECTIVITY_CHANGE
的action,因为当网络状态变化时,会发出这么一个广播,这里注册的意思就是接收这个广播registerReceiver(networkChangeReceiver, intentFilter)
还有在onDestroy方法中取消注册。
还有获取网络状态需要申请权限,在AndroidManifest.xml文件中添加<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
静态注册开机启动
动态注册很灵活,但是程序必须启动后才能接收到广播,静态注册可以在未启动就能接受广播
新建一个BootCompleteReceiver,在AndroidManifest.xml文件中注册,如果是用Android Studio快捷方式新建的类已经注册好了,在Application标签下
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
还需要声明权限<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
这样就可以自启动了
在广播的onReceive方法中不要写太多的逻辑和耗时的操作,不允许开启线程,运行较长时间没结束会报错
所以广播的作用大多是创建一条通知栏,开启一个服务等
发送标准广播
定义一个接收器MyBroadcastReceiver
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_LONG).show();
}
}
注册
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="org.nuaa.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>
主活动中添加一个按钮,点击发送广播
Button button = (Button) findViewById(R.id.send);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("org.nuaa.broadcasttest.MY_BROADCAST");
sendBroadcast(intent);
}
});
注意,在高版本的Android系统中,静态广播诸多限制,建议改成动态注册广播,或者加上intent.setComponent(new ComponentName("pckname", "receiver path"));
,不过这样的话就只能针对一个广播接收器发送了,目前暂不知道解决方案
发送有序广播
改成sendOrderBroadcast,并且在静态注册处添加优先级<intent-filer priority="100">
在接收器代码添加abortBroadcast()
可以截断广播
本地广播
似乎没有该机制了,暂时不写
广播实践 -- 强制下线功能
一个ActivityCollector管理所有活动
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<>();
public static void addActivity(Activity activity) {
activities.add(activity);
}
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
public static void finishAll() {
for (Activity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();;
}
}
activities.clear();
}
}
创建BaseActivity基类作为所有活动的父类
public class BaseActivity extends AppCompatActivity {
private ForceOfflineReceiver receiver;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("org.nuaa.broadcasttest2.FORCE_OFFLINE");
receiver = new ForceOfflineReceiver();
registerReceiver(receiver, intentFilter);
}
@Override
protected void onPause() {
super.onPause();
if (receiver != null) {
unregisterReceiver(receiver);
receiver = null;
}
}
class ForceOfflineReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Warning");
builder.setMessage("You are forced to be offline");
builder.setCancelable(false);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCollector.finishAll();
Intent intent = new Intent(context, LoginActivity.class);
context.startActivity(intent);
}
});
builder.show();
}
}
}
在基类中创建强制下线广播接收器,onReceive方法中代码逻辑是弹出一个对话框,setCacleable为false即不可取消,OK按钮点击后关闭所有活动,并重新启动登录活动
新建LoginActivity类,继承BaseActivity
public class LoginActivity extends BaseActivity {
private EditText accountEdit;
private EditText passwdEdit;
private Button login;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
accountEdit = (EditText) findViewById(R.id.account);
passwdEdit = (EditText) findViewById(R.id.passwd);
login = (Button) findViewById(R.id.login);
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String account = accountEdit.getText().toString();
String passwd = passwdEdit.getText().toString();
if (account.equals("admin") && passwd.equals("123")) {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(LoginActivity.this, "error", Toast.LENGTH_SHORT).show();
}
}
});
}
}
界面布局如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal">
<TextView
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="18sp"
android:text="Account:"/>
<EditText
android:id="@+id/account"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"/>
</LinearLayout>
<LinearLayout
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal">
<TextView
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="18sp"
android:text="Password:"/>
<EditText
android:id="@+id/passwd"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="textPassword"
android:layout_gravity="center_vertical"/>
</LinearLayout>
<Button
android:id="@+id/login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/action_sign_in" />
</LinearLayout>
主界面只有一个按钮,点击强制下线
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button offline = (Button) findViewById(R.id.offline);
offline.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("org.nuaa.broadcasttest2.FORCE_OFFLINE");
sendBroadcast(intent);
}
});
}
}
最后把主活动改成LoginActivity
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>