android入门学习 -- 3 -- 碎片Fragment&广播Broadcast

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())替换

动态添加碎片的步骤:

  1. 创建待添加的碎片实例replaceFragment(new XXXXX())
  2. 获取FragmentManager,在活动中可以直接通过调用getSupportFragmentManager()方法获取
  3. 开启一个事务,通过调用beginTransaction()方法开启
  4. 向容器内添加或替换碎片,使用replace(),传入容器的id和代添加的碎片实例
  5. 提交事务,commit()

在碎片中模拟返回栈

在commit()之前添加一句transaction.addToBackStack(null),参数是String名字,用于描述返回栈的状态。

碎片和活动之间通信

活动中调用碎片

RightFragment rightFragment = (RightFragment) getSupportFragmentManager().findFragmentById(R.id.right_fragment);

碎片中调用活动

MainActivity activity = (MainActivity) getActivity();

有活动实例后,调用活动中的方法就容易了。另外当碎片中使用Context对象时,也可以用getActivity(),因为活动本身就是一个Context对象

碎片生命周期

运行,暂停,停止,销毁

  1. 运行
    碎片可见,关联的活动处于运行状态,碎片也处于运行
  2. 暂停
    活动进入暂停状态时(由于另外一个未占满屏幕的活动被添加到栈顶),相关联的碎片就会进入暂停
  3. 停止
    当活动进入暂停状态,关联的碎片就会进入停止状态,或,
    通过调用FragmentManager.remove()、repalce()方法将碎片从活动中移除,如果事务在提交前调用addToBackStack(),碎片也会进入停止状态,对用户完全不可见,有可能被系统回收
  4. 销毁
    碎片总是依附于活动,活动销毁,碎片也会销毁,或,
    通过调用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>
上一篇:DWR技术实现消息已读后更新所有打开页面消息数功能


下一篇:Python 2 退休