Android网络操作与数据存储(一):Android网络与通信

目录

第一节:网络操作

一、获取网络数据

获取数据步骤:

注意事项:

获取数据代码

二、解析JSON数据

1、使用Json解析

2、使用Gson解析

GsonFormat插件的使用 

第二节:Handler通信

一、handler功能

二、handler常用方法和属性

三、Message的方法与属性

四、handler的使用

1、更新UI线程。

2、延时执行某一操作。

五、关于handler的内存泄漏

原理:

危害:

解决办法:

 

第三节:AsyncTask通信

AsyncTask子类的泛型参数

AsyncTask子类的回调方法

第四节:ListView展示列表数据

一、使用ListView获取应用程序列表

二、ListView实现隔行效果

第五节:GridView实现九宫格

一、GridView常用属性

二、ArrayAdapter适配器

三、使用GridView显示应用程序信息

四、使用GridView显示网络图片

第六节:CardView实现卡片布局效果

一、CardView是什么?

二、CardView常用属性

三、一个简单的案例


第一节:网络操作

一、获取网络数据

获取数据步骤:

1、实例化URL对象。

2、获取HttpUrlConnection对象。

3、设置请求连接属性。

4、获取响应码、判断连接结果码。

connection.setReadTimeout(5000);
connection.setRequestMethod("GET");

connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Charset", "UTF-8");
connection.setRequestProperty("Accept-Charset", "UTF-8");

5、读取输入流并解析。

注意事项:

1、网络操作需要在子线程中进行。

2、需要获取网络权限。

<uses-permission android:name="android.permission.INTERNET"/>

3、子线程不能直接更新UI线程控件数据。

runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mTvShowData.setText(mResult);
        }
    });

获取数据代码

URL url=new URL(str);//str为网址
HttpURLConnection connection= (HttpURLConnection) url.openConnection();
connection.setReadTimeout(5000);
connection.setRequestMethod("GET");
if(connection.getResponseCode()==200){
   InputStream inputStream=connection.getInputStream();
    byte[] bytes=new byte[1024];
    int len=0;
    ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
    while((len=inputStream.read(bytes))!=-1) {
        outputStream.write(bytes,0,len);
    }
    result=outputStream.toString();
    inputStream.close();
    outputStream.close();
    return result;
}

5、检查网络连接

1)  ConnectivityManager:它会回答关于网络连接的查询结果,并在网络连接改变时通知应用程序。
2)  NetworkInfo:描述一个给定类型(移动网络或 Wi-Fi 等)的网络接口状态。下面这个方法可以找到的第一个已连接的网络接口,如果返回 null,则表示没有已连接的网络接口(意味着网络连接不可用):

public boolean isOnline(){
    ConnectivityManager connMgr=(ConnectivityManager)getSystemServic(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo=connMgr.getActiveNetworkInfo();
    return (networkInfo!=null && networkInfo.isConnected());
}

二、解析JSON数据

1、使用Json解析

1、使用json类型字符串创建JSONObject数据。

2、[]中的数据使用getJSONArray获取。

3、使用getInt、getString等获取json中的数据。

    JSONObject obj=new JSONObject(mResult);
    JSONArray datas=obj.getJSONArray("data");
    StringBuilder builder=new StringBuilder();
    for (int i = 0; i < datas.length(); i++) {
        JSONObject data=datas.getJSONObject(i);
        int id=data.getInt("id");
        String name=data.getString("name");
        String description=data.getString("description");
        builder.append("编号:"+id+"\n");
        builder.append("名称:"+name+"\n");
        builder.append("描述:"+description+"\n");
    }
    mTvShowData.setText(builder.toString());

2、使用Gson解析

1、导入gson依赖包。

2、创建json数据对应的类,成员名称必须相同。

3、可以使用嵌套、可以获取数组。

public class Essay {
    int id;
    String name;
    String picSmall;
    String picBig;
    String description;
    int learner;
    //getter & setter 省略
}
public class Chapter{
    int status;
    List<Essay> data;
    String msg;
    //getter & setter 省略
}

//使用Gson解析
Gson gson=new Gson();
Chapter chapter=gson.fromJson(jsonString, Chapter.class);
List<Essay> datas=chapter.getData();
StringBuilder builder=new StringBuilder();
for (int i = 0; i < datas.size(); i++) {
    Essay data=datas.get(i);
    builder.append("编号:"+data.getId()+"\n");
    builder.append("名称:"+data.getName()+"\n");
    builder.append("描述:"+data.getDescription()+"\n");
}
mTvShowData.setText(builder.toString());

*注:上面代码中成员变量也可以随意命名,然后通过注解实现json数据同名

public class Essay {
    @SerializedName("id")
    int mId;
    @SerializedName("name")
    String mName;
    @SerializedName("picSmall")
    String mPicSmall;
    @SerializedName("picBig")
    String mPicBig;
    @SerializedName("description")
    String mDescription;
    @SerializedName("learner")
    int mLearner;
    //getter & setter 省略
}
public class Chapter{
    @SerializedName("status")
    int mStatus;
    @SerializedName("data")
    List<Essay> mData;
    @SerializedName("msg")
    String mMsg;
    //getter & setter 省略
}

GsonFormat插件的使用 

GsonFormat插件可以快速生成json数据的实体类。

1、Settings->plugins->marketplace 搜索GsonFormat,下载然后重启AS

2、新建一个类,名字随意,在代码中使用快捷键Alt+insert调出菜单 选择GsonFormat ,粘贴json数据,next。。。

第二节:Handler通信

一、handler功能

主要用于异步消息的处理,包括发送消息和处理消息两部分。发送消息函数将消息发送到队列中,处理消息函数从队列中取出消息执行,这个过程是异步执行的,适合处理耗时较长的操作。

二、handler常用方法和属性

boolean handler.post(Runnable r)

boolean handler.postDelay(Runnable r,long delayMillis)

boolean handler.sendMessage(Message msg)

boolean handler.sendMessageDelay(Message msg,long delayMillis)

void handler.handlerMessage(Message msg) // 处理消息

void handler.removeCallbacks(Runnable r);

Message handler.obtainMessage();//获取消息,内部调用的是Message.obtain()

三、Message的方法与属性

Message Message.obtain();//获取消息

Message.what //int

Message.obj //object

Message.arg1 //int 

Message.arg2 //int

四、handler的使用

1、更新UI线程。

Android中为了保证UI线程安全,所有子线程不允许直接更新UI,所有的UI操作都要通过Handler传递消息到主线程,由主线程按照消息队列顺序轮流执行UI操作。

注:runOnUiThread(Runnable r) 也是用来更新UI操作,内部机制与hanlder相同。如果方法在UI线程中调用则立即执行,在子线程调用则向消息队列发送消息,等待主线程处理。

 

2、延时执行某一操作。

闪屏页的实现,通过handler.postDelay(Runnable r,long delayMillis)方法延迟启动主界面。

五、关于handler的内存泄漏

原理:

Java中一个对象不被任何引用所指向,则该对象会在被GC发现的时候会被回收。创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(用来更新UI)。handler通常用来执行耗时操作,如果在这个过程中用户关闭了Activity,由于这个Activity持有handler的引用,会导致Activity无法被回收(内存泄漏)。

危害:

使程序占用内存过高,内存溢出,程序报错,如果反复运行程序会导致Android内存使用超过系统限制。

解决办法:

方法一:通过程序逻辑来进行保护。
1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。 

2.如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。

方法二:将Handler声明为静态类。
PS:在Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,静态的内部类不会持有外部类的引用。
静态类不持有外部类的对象,所以你的Activity可以随意被回收。由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference)。

 

第三节:AsyncTask通信

AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类.通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程.

AsyncTask子类的泛型参数

AsyncTask<Params,Progress,Result>是一个抽象类,通常用于被继承.继承AsyncTask需要指定如下三个泛型参数:

Params:启动任务时输入的参数类型

Progress:后台任务执行中返回进度值的类型

Result:后台任务执行完成后返回结果的类型

AsyncTask子类的回调方法

Result doInBackground(Params p):必须重写,异步执行后台线程要完成的任务,耗时操作将在此方法中完成。参数p为泛型的第一个参数,实例化AsyncTask类后,由execute(Params p)方法传入;返回值为泛型的第三个参数类型。

void onPreExecute():执行后台耗时操作前被调用,通常用于进行初始化操作。

void onPostExecute(Result r):当doInBackground方法完成后,系统将自动调用此方法,并将doInBackground方法返回的值传入此方法.通过此方法进行UI的更新。

void onProgressUpdate(Progress p):当在doInBackground方法中调用publishProgress(p)方法更新任务执行进度后,将调用此方法.通过此方法我们可以知晓任务的完成进度。

 

第四节:ListView展示列表数据

一、使用ListView获取应用程序列表

1、在Layout中创建ListView并在代码中获取对象。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/appinfo_lv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"></ListView>

</LinearLayout>
mLvAppInfo = findViewById(R.id.appinfo_lv);

2、创建每一行的Layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    <ImageView
        android:id="@+id/im_item"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@mipmap/icon01"/>
    <TextView
        android:id="@+id/tv_item"
        android:gravity="center_vertical"
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:text="第一个图标"/>
</LinearLayout>

3、创建每一行的数据

private List<ResolveInfo> mAppInfos = getAppInfos();

/**
* 获取所有的应用信息的方法
* @return
*/
private List<ResolveInfo> getAppInfos(){
    Intent intent=new Intent(Intent.ACTION_MAIN,null);
    intent.addCategory(Intent.CATEGORY_LAUNCHER);
    return getPackageManager().queryIntentActivities(intent,0);
}

4、用adapter将数据填充到每一行的视图中

    //自定义适配器
    public class ListAdapter extends BaseAdapter {
        //private List<String> mAppNames;
        private List<ResolveInfo> mAppInfos;

        public ListAdapter(List<ResolveInfo> appInfos) {
            //mAppNames = appNames;
            mAppInfos=appInfos;
        }

        @Override
        public int getCount() {
            return mAppInfos.size();
        }

        @Override
        public Object getItem(int i) {
            return mAppInfos.get(i);
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public View getView(final int i, View view, ViewGroup viewGroup) {
            ViewHolder viewHolder=new ViewHolder();
            if(view==null) {
                LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.item_list_view, null);
                viewHolder.mTextView=view.findViewById(R.id.tv_item);
                viewHolder.mImageView=view.findViewById(R.id.im_item);
                view.setTag(viewHolder);
            }else{
                viewHolder= (ViewHolder) view.getTag();
            }
            viewHolder.mTextView.setText(mAppInfos.get(i).activityInfo.loadLabel(getPackageManager()));
            viewHolder.mImageView.setImageDrawable(mAppInfos.get(i).activityInfo.loadIcon(getPackageManager()));

            //点击事件
            viewHolder.mTextView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    /*String packageName=mAppInfos.get(i).activityInfo.packageName;
                    String className=mAppInfos.get(i).activityInfo.name;
                    ComponentName componentName=new ComponentName(packageName,className);
                    Intent intent=new Intent();
                    intent.setComponent(componentName);
                    startActivity(intent);*/
                }
            });


            return view;
        }
        public class  ViewHolder{
            TextView mTextView;
            ImageView mImageView;
        }
    }

5、为ListView添加HeaderView

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@mipmap/icon01"/>
</LinearLayout>
LayoutInflater inflater=getLayoutInflater();
//LayoutInflater inflater1=LayoutInflater.from(this);
View view=inflater.inflate(R.layout.header_list_view,null);
mLvAppInfo.addHeaderView(view);

二、ListView实现隔行效果

当一个ListView中包含多种样式的item时,比如聊天列表中发送和接收的item应该设计成两种不同样式,如果聊天包含发送图片功能还应该增加样式,这种情况应该使用适配器的getViewTypeCount和getItemViewType两个方法解决。

getViewTypeCount 返回样式的个数。如包含发送消息、接收消息、发送图片、接受图片四种样式,则此处代码return 4;

getItemViewType 返回样式的序号。使用时根据数据项中的内容返回不同的整数(从0开始),然后在getView中通过switch(getItemViewType(position))来分别加载不同的布局。

public class ChatAdapter extends BaseAdapter {
    private List<ChatMessage> mChatMessageList;
    private LayoutInflater mInflater;

    public ChatAdapter(Context context, List<ChatMessage> chatMessageList) {
        mChatMessageList = chatMessageList;
        mInflater=LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        return mChatMessageList.size();
    }

    @Override
    public Object getItem(int position) {
        return mChatMessageList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder=null;
        ChatMessage chatMessage=mChatMessageList.get(position);
        int viewType = getItemViewType(position);
        if(convertView==null){
            switch (viewType){
                    case IMessageViewType.COM_MESSAGE:
                        convertView = mInflater.inflate(R.layout.chatting_item_msg_text_left, null);
                        break;
                    case IMessageViewType.TO_MESSAGE:
                        convertView = mInflater.inflate(R.layout.chatting_item_msg_text_right, null);
                        break;
                }
            holder=new ViewHolder();
            holder.mIcon=convertView.findViewById(R.id.icon_iv);
            holder.mName=convertView.findViewById(R.id.name_tv);
            holder.mContent=convertView.findViewById(R.id.content_tv);
            holder.mTime=convertView.findViewById(R.id.time_tv);
            convertView.setTag(holder);
        }else{
            holder= (ViewHolder) convertView.getTag();
        }

        holder.mTime.setText(chatMessage.getDate());
        if(chatMessage.isComeMessage()){
            holder.mIcon.setImageResource(R.mipmap.girl_icon);
        }else{
            holder.mIcon.setImageResource(R.mipmap.boy_icon);
        }
        holder.mName.setText(chatMessage.getName());
        holder.mContent.setText(chatMessage.getContent());

        return convertView;
    }

    @Override
    public int getViewTypeCount() {
        return 2;
    }

    @Override
    public int getItemViewType(int position) {
        ChatMessage chatMessage=mChatMessageList.get(position);
        return chatMessage.isComeMessage()?IMessageViewType.TO_MESSAGE:IMessageViewType.COM_MESSAGE;
    }
    
    interface IMessageViewType{
        int COM_MESSAGE = 0;
        int TO_MESSAGE = 1;
    }
    
    class ViewHolder{
        ImageView mIcon;
        TextView mTime;
        TextView mName;
        TextView mContent;
    }
}

第五节:GridView实现九宫格

一、GridView常用属性

android:columnWidth="90dp" 每一列的宽度

android:numColumns="4 | auto_fit" 列宽  auto_fit(列数根据屏幕大小自适应)

android:verticalSpacing="10dp" 垂直方向的间距

android:horizontalSpacing="10dp" 水平方向的间距

android:stretchMode = "columnWidth | spacingWidth" 屏幕两侧剩余宽度平均分配给列宽(columnWidth)或者间距(spacingWidth)

二、ArrayAdapter适配器

使用ArrayAdapter将文字显示成九宫格形式

List<String> strList=new ArrayList<String>();
for(int i=0;i<3;i++){
    strList.add("测试"+i);
}
ArrayAdapter<String> arrayAdapter=new ArrayList<String>(this,android.R.layout.simple_list_item_1,strList);
mGridView.setAdapter(arrayAdapter);

三、使用GridView显示应用程序信息

public List<AppInfo> getAppList(){
        List<AppInfo> appInfoList=new ArrayList<AppInfo>();
        PackageManager packageManager=getPackageManager();
        List<PackageInfo> installedPackages = packageManager.getInstalledPackages(0);
        for (int i = 0; i < installedPackages.size(); i++) {
            PackageInfo packageInfo=installedPackages.get(i);
            AppInfo appInfo=new AppInfo();
            appInfo.setAppIcon(packageInfo.applicationInfo.loadIcon(packageManager));
            appInfo.setAppName(packageInfo.applicationInfo.loadLabel(packageManager).toString());
            appInfo.setPackageName(packageInfo.packageName);
            if((packageInfo.applicationInfo.flags& ApplicationInfo.FLAG_SYSTEM)==0)
            {
                appInfoList.add(appInfo);
            }
        }
        return appInfoList;
    }
        //初始化数据
        List<AppInfo> appInfoList=getAppList();

        AppInfoAdapter adapter=new AppInfoAdapter(this,appInfoList);
        mGridView.setAdapter(adapter);
public class AppInfoAdapter extends BaseAdapter {
    private Context mContext;
    private List<AppInfo> mAppInfoList;

    public AppInfoAdapter(Context context, List<AppInfo> appInfoList) {
        mContext = context;
        mAppInfoList = appInfoList;
    }

    @Override
    public int getCount() {
        return mAppInfoList.size();
    }

    @Override
    public Object getItem(int i) {
        return mAppInfoList.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder holder;
        if(view==null){
            view=View.inflate(mContext, R.layout.item_grid_appinfo,null);
            holder=new ViewHolder();
            holder.mAppIcon=view.findViewById(R.id.appicon_iv);
            holder.mAppName=view.findViewById(R.id.appname_tv);
            holder.mPackageName=view.findViewById(R.id.pkname_tv);
            view.setTag(holder);
        }else{
            holder= (ViewHolder) view.getTag();
        }

        AppInfo appInfo=mAppInfoList.get(i);
        holder.mAppIcon.setImageDrawable(appInfo.getAppIcon());
        holder.mAppName.setText(appInfo.getAppName());
        holder.mPackageName.setText(appInfo.getPackageName());

        return view;
    }

    private class ViewHolder{
        ImageView mAppIcon;
        TextView mAppName;
        TextView mPackageName;
    }
}
public class AppInfo {
    private String appName;
    private String packageName;
    private Drawable appIcon;

    public String getAppName() {
        return appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

    public String getPackageName() {
        return packageName;
    }

    public void setPackageName(String packageName) {
        this.packageName = packageName;
    }

    public Drawable getAppIcon() {
        return appIcon;
    }

    public void setAppIcon(Drawable appIcon) {
        this.appIcon = appIcon;
    }
}

四、使用GridView显示网络图片

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_grid_net_image);

        //初始化控件
        mGridView = findViewById(R.id.grid_netimage_gv);

        //初始化数据
        List<String> mImgList = new ArrayList<>();
        mImgList.add("http://img5.duitang.com/uploads/item/201406/26/20140626164837_dzKds.jpeg");
        mImgList.add("http://img2.imgtn.bdimg.com/it/u=3980629563,3881837630&fm=21&gp=0.jpg");
        mImgList.add("http://img5q.duitang.com/uploads/item/201505/08/20150508155052_XJaNW.jpeg");
        mImgList.add("http://img4.duitang.com/uploads/item/201407/02/20140702105736_FdN5P.jpeg");
        mImgList.add("http://img2.imgtn.bdimg.com/it/u=2866652161,3841912673&fm=21&gp=0.jpg");
        mImgList.add("http://img4.imgtn.bdimg.com/it/u=883757693,2063816225&fm=21&gp=0.jpg");
        mImgList.add("http://cdn.duitang.com/uploads/item/201309/26/20130926110955_QtUdX.jpeg");
        mImgList.add("http://zjimg.5054399.com/allimg/160815/14_160815161625_9.jpg");
        mImgList.add("http://i-7.vcimg.com/trim/09ce7067d2467c54cf05bbd271ee3ec8430415/trim.jpg");

        mImageInfos = new ArrayList<>();
        for (int i = 0; i < mImgList.size(); i++) {
            ImageInfo imageInfo=new ImageInfo();
            imageInfo.setImagePath(mImgList.get(i));
            imageInfo.setName("图片"+i);
            mImageInfos.add(imageInfo);
        }

        ImageAdapter adapter=new ImageAdapter(this,mImageInfos);
        mGridView.setAdapter(adapter);
    }
public class ImageAdapter extends BaseAdapter {
    private Context mContext;
    private List<ImageInfo> mImageInfoList;

    public ImageAdapter(Context context, List<ImageInfo> imageInfoList) {
        mContext = context;
        mImageInfoList = imageInfoList;
    }

    @Override
    public int getCount() {
        return mImageInfoList.size();
    }

    @Override
    public Object getItem(int i) {
        return mImageInfoList.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder holder;
        if(view==null){
            holder=new ViewHolder();
            view=View.inflate(mContext, R.layout.item_grid_image,null);
            holder.mImageView=view.findViewById(R.id.image_iv);
            holder.mTextView=view.findViewById(R.id.text_tv);
            view.setTag(holder);
        }else{
            holder= (ViewHolder) view.getTag();
        }

        ImageInfo imageInfo=mImageInfoList.get(i);
        holder.mTextView.setText(imageInfo.getName());
        //RequestOptions options=new RequestOptions();
        Glide.with(mContext).load(imageInfo.getImagePath()).into(holder.mImageView);

        return view;
    }

    private class ViewHolder{
        ImageView mImageView;
        TextView mTextView;
    }
}
public class ImageInfo {
    private String mImagePath;
    private String mName;

    public String getImagePath() {
        return mImagePath;
    }

    public void setImagePath(String imagePath) {
        mImagePath = imagePath;
    }

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        mName = name;
    }
}

第六节:CardView实现卡片布局效果

一、CardView是什么?

1、Android 5.0 之后新增

2、android support v7包 独立引入

3、继承自FrameLayout,方便作为其他控件容器,添加3D阴影和圆角效果

二、CardView常用属性

1、cardBackgroundColor 设置背景颜色

2、cardCornerRadius 设置圆角半径

3、contentPadding 设置内部padding

4、cardElevation 设置阴影大小

5、cardUseCompatPadding 默认为false,用于5.0及以上,true则添加额外padding绘制阴影

6、cardPreventCornerOverlap 默认为true,用于5.0以下,添加额外的padding,防止内容和圆角重叠

三、一个简单的案例

使用前需要导包,搜索cardview

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <android.support.v7.widget.CardView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:cardBackgroundColor="#44ff0000"
        app:cardCornerRadius="10dp"
        app:cardElevation="10dp"
        android:layout_gravity="center">
        <TextView
            android:layout_width="200dp"
            android:layout_height="50dp"
            android:text="Hello World!"
            android:gravity="center" />
    </android.support.v7.widget.CardView>
</FrameLayout>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值