Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all 5930 articles
Browse latest View live

Android相机相册的调用,图片的存储删除

$
0
0

主要以SimpleFilter的源码为例子详解,app可在百度应用商店下载
SimpleFilter源代码下载

xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    <ImageView 
        android:id="@+id/iv_change"
        android:layout_width="fill_parent"
        android:scaleType="fitXY"
        android:layout_height="220dp"
        android:maxWidth="1000dp"
        android:maxHeight="1000dp"
        android:src="@drawable/origin"
/>
    <SeekBar 
        android:id="@+id/sb_alpha"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:layout_margin="10dp"
        />
    <SeekBar 
        android:id="@+id/sb_red"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:layout_margin="10dp"

        />
        <SeekBar 
        android:id="@+id/sb_green"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:layout_margin="10dp"/>
    <SeekBar 
        android:id="@+id/sb_blue"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:layout_margin="10dp"/>
    <TextView 
        android:id="@+id/tv_display"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="8dp"
        android:textSize="16sp"
        android:textColor="#333333"/>
    <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button 
            android:id="@+id/btn_photo"
            android:layout_height="40dp"
            android:layout_width="0dp"
            android:layout_marginLeft="4dp"
            android:text="拍 照"
            android:textColor="#fff"
            android:background="@xml/btn_blue"
            android:layout_weight="1"/>
        <Button 
            android:id="@+id/btn_picture"
            android:layout_height="40dp"
            android:layout_width="0dp"
            android:layout_marginLeft="4dp"
            android:layout_weight="1"
            android:textColor="#fff"
            android:layout_marginRight="4dp"
            android:background="@xml/btn_blue"
            android:text="相 册"/>
    </LinearLayout>
        <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:orientation="horizontal">
            <Button 
            android:id="@+id/btn_save"
            android:layout_height="40dp"
            android:layout_width="0dp"
            android:textColor="#fff"
            android:layout_marginLeft="4dp"
            android:layout_weight="1"
            android:background="@xml/btn_blue"
            android:text="保 存"/>
        <Button 
            android:id="@+id/btn_origin"
            android:layout_height="40dp"
            android:layout_width="0dp"
            android:textColor="#fff"
            android:layout_marginLeft="4dp"
            android:layout_marginRight="4dp"
            android:layout_weight="1"
            android:background="@xml/btn_blue"
            android:text="初始化"/>
        </LinearLayout>
         <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:orientation="horizontal">
        <Button 
            android:id="@+id/btn_delete"
            android:layout_height="40dp"
            android:layout_width="0dp"
            android:layout_marginLeft="4dp"
            android:text="清除保存文件"
            android:textColor="#fff"
            android:background="@xml/btn_blue"
            android:layout_weight="1"/>
        <Button 
            android:id="@+id/btn_see"
            android:layout_height="40dp"
            android:layout_width="0dp"
            android:layout_marginLeft="4dp"
            android:layout_weight="1"
            android:textColor="#fff"
            android:layout_marginRight="4dp"
            android:background="@xml/btn_blue"
            android:text="查看保存文件"/>
</LinearLayout>
</LinearLayout>

java

package com.example.mpaint;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements OnSeekBarChangeListener,
        OnClickListener {
    private ImageView iv_change;
    private SeekBar sb_alpha, sb_red, sb_green, sb_blue;
    private TextView tv_dispaly;
    private float a = 0f, r = 0f, g = 0f, b = 0f;
    private String Path;
    private Button btn_photo, btn_picture, btn_save, btn_origin,btn_delete,btn_see;
    private Bitmap photo = null,photoorigin;
    private File mCurrentPhotoFile;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        //新建图片存储的目录
        Path = Environment.getExternalStorageDirectory().toString()
                + "/mFilter";
        File file = new File(Path);
        if (!file.exists()) {
            file.mkdir();
        }
        iv_change = (ImageView) findViewById(R.id.iv_change);
        tv_dispaly = (TextView) findViewById(R.id.tv_display);
        tv_dispaly.setText("a: " + String.valueOf((a)) + "%     " + "r: "
                + String.valueOf((r)) + "     " + "g: " + String.valueOf((g))
                + "     " + "b: " + String.valueOf((b)));
        //从资源文件中获取图片
        photoorigin=BitmapFactory.decodeResource(getResources(), R.drawable.origin);
        //初始化进度条
        initSeek();
        //初始化按钮
        initButton();
    }

    private void initButton() {
        btn_save = (Button) findViewById(R.id.btn_save);
        btn_save.setOnClickListener(this);
        btn_photo = (Button) findViewById(R.id.btn_photo);
        btn_photo.setOnClickListener(this);
        btn_picture = (Button) findViewById(R.id.btn_picture);
        btn_picture.setOnClickListener(this);
        btn_origin = (Button) findViewById(R.id.btn_origin);
        btn_origin.setOnClickListener(this);
        btn_delete = (Button) findViewById(R.id.btn_delete);
        btn_delete.setOnClickListener(this);
        btn_see = (Button) findViewById(R.id.btn_see);
        btn_see.setOnClickListener(this);
    }

    private void initSeek() {
        sb_alpha = (SeekBar) findViewById(R.id.sb_alpha);
        sb_alpha.setOnSeekBarChangeListener(this);
        sb_red = (SeekBar) findViewById(R.id.sb_red);
        sb_red.setOnSeekBarChangeListener(this);
        //设置进度条默认位置
        sb_red.setProgress(50);
        sb_green = (SeekBar) findViewById(R.id.sb_green);
        sb_green.setOnSeekBarChangeListener(this);
        sb_green.setProgress(50);
        sb_blue = (SeekBar) findViewById(R.id.sb_blue);
        sb_blue.setOnSeekBarChangeListener(this);
        sb_blue.setProgress(50);
    }
//重写OnSeekBarChangeListener
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress,
            boolean fromUser) {
        switch (seekBar.getId()) {
        case R.id.sb_alpha:
            a = (float) (progress / 100.0);
            break;
        case R.id.sb_red:
            r = (float) ((progress - 50) / 5.0);
            break;
        case R.id.sb_green:
            g = (float) ((progress - 50) / 5.0);
            break;
        case R.id.sb_blue:
            b = (float) ((progress - 50) / 5.0);
            break;

        default:
            break;
        }
        setArgb(1 - a, r + 1, g + 1, b + 1);
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }

    public void setArgb(float alpha, float red, float green, float blue) {
        tv_dispaly.setText("a: " + String.valueOf((a * 100)) + "%     " + "r: "
                + String.valueOf((r)) + "     " + "g: " + String.valueOf((g))
                + "     " + "b: " + String.valueOf((b)));
        //新建颜色矩阵
        ColorMatrix mColorMatrix = new ColorMatrix(new float[] { red, 0, 0, 0,
                0, 0, green, 0, 0, 0, 0, 0, blue, 0, 0, 0, 0, 0, alpha, 0 });
        //使用颜色矩阵过滤图片
        iv_change.getDrawable().setColorFilter(
                new ColorMatrixColorFilter(mColorMatrix));
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.btn_origin:
            a = 0f;
            r = 0f;
            g = 0f;
            b = 0f;
            tv_dispaly.setText("a: " + String.valueOf((a)) + "%     " + "r: "
                    + String.valueOf((r)) + "     " + "g: " + String.valueOf((g))
                    + "     " + "b: " + String.valueOf((b)));
            sb_alpha.setProgress(0);
            sb_red.setProgress(50);
            sb_green.setProgress(50);
            sb_blue.setProgress(50);
            //初始化图片
            if(photo!=null) {
            iv_change.setBackgroundColor(Color.parseColor("#00000000"));
            iv_change.setImageBitmap(photo);}
            else{
                iv_change.setBackgroundColor(Color.parseColor("#00000000"));
                iv_change.setImageBitmap(photoorigin);
            }
            break;
        case R.id.btn_save:
            saveImageView();
            break;
        case R.id.btn_photo:
            takephoto();
            break;
        case R.id.btn_picture:
            takepicture();
            break;
        case R.id.btn_see:
            see();
            break;
        case R.id.btn_delete:
            new AlertDialog.Builder(MainActivity.this)
            .setTitle("提示信息")
            .setMessage("是否删除路径"+Path+"下的所有文件"+"?")
            .setNegativeButton("确认",new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    delete();
                    dialog.dismiss();
                    //判断是否完全删除
                    File file = new File(Path);
                    File[] childFile = file.listFiles();
                    if (childFile.length==0) {
                        Toast.makeText(MainActivity.this, "删除成功", Toast.LENGTH_LONG).show();
                    }else {
                        Toast.makeText(MainActivity.this, "删除失败", Toast.LENGTH_LONG).show();
                    }
                }
            })
            .setPositiveButton("取消",new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                }
            }).show();
            break;

        default:
            break;
        }
    }
//查看文件
    private void see() {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_GET_CONTENT);
        File file = new File(Path);
        intent.setDataAndType(Uri.fromFile(file),  "*/*");
        startActivityForResult(intent, 2);
    }

    private void delete() {
        File file = new File(Path);
        File[] childFile = file.listFiles();
        for (File file2 : childFile) {
            file2.delete();
        }
    }
    private void takepicture() {
    //相册
        Intent intentPick = new Intent(Intent.ACTION_PICK, null);
        intentPick.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                "image/*");
        startActivityForResult(intentPick, 0);
    }

    private void takephoto() {
    //照相
        mCurrentPhotoFile= new File(Path,
                "temp.jpg");
        Intent intent = new Intent(
                MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(mCurrentPhotoFile));
        startActivityForResult(intent, 1);
    }

    private void saveImageView() {
    //自定义文件名
        final String path = Path  +"/" +getNowDateTime("yyyyMMddHHmmss")
                + ".jpg";
        new AlertDialog.Builder(MainActivity.this)
        .setTitle("提示信息")
        .setMessage("是否将文件保存到"+Path+"?")
        .setNegativeButton("确认",new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                //设置view可缓存.然后以bitmap获取view的缓存
                iv_change.setDrawingCacheEnabled(true);
                Bitmap photofinally = iv_change.getDrawingCache();
                try {
                    BufferedOutputStream bos = new BufferedOutputStream(
                            new FileOutputStream(path));
                    //将bitmap压缩存储
                    photofinally.compress(Bitmap.CompressFormat.JPEG, 100, bos);
                    bos.flush();
                    bos.close();
                    bos = null;
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                iv_change.setDrawingCacheEnabled(false);
                File file = new File(path);
                dialog.dismiss();
                if (file.exists()) {
                    Toast.makeText(MainActivity.this, "保存成功", Toast.LENGTH_LONG).show();
                }else {
                    Toast.makeText(MainActivity.this, "保存失败", Toast.LENGTH_LONG).show();
                }
            }
        })
        .setPositiveButton("取消",new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        }).show();
    }

    public static String getNowDateTime(String format) {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat(format);// "yyyyMMdd"
        String dateString = formatter.format(currentTime);
        return dateString;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        case 0:
            if (data != null) {
            //相册uri获取
                Uri uri = data.getData();
                //共享相机获取的数据
                ContentResolver cr = this.getContentResolver();
                try {
                //回收bitmap
                    if (photo != null && !photo.isRecycled()) {
                        photo.recycle();
                        System.gc();
                    }
                    //将好;获取的数据编码成bitmap
                    photo = BitmapFactory.decodeStream(cr.openInputStream(uri));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                iv_change.setBackgroundColor(Color.parseColor("#00000000"));
                iv_change.setImageBitmap(photo);
            }
            break;
        case 1:
        //resultCode=-1为成功
            if (resultCode==-1) {
            ContentResolver cr = getContentResolver();
            Uri fileUri = Uri.fromFile(mCurrentPhotoFile);
            //刷新单个文件
            sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,fileUri));
            //刷新sd卡
            //sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, dirUri));
                try {
                    if (photo != null && !photo.isRecycled()) {
                        photo.recycle();
                        System.gc();
                    }
                    photo = BitmapFactory.decodeStream(cr.openInputStream(fileUri));
                } catch (Exception e) {
                    e.printStackTrace();
                }

                iv_change.setBackgroundColor(Color.parseColor("#00000000"));
                iv_change.setImageBitmap(photo);
            }
            break;
        case 2:
            break;
        default:
            break;
        }

        super.onActivityResult(requestCode, resultCode, data);
    }
}
作者:shichen501 发表于2016/9/20 19:31:01 原文链接
阅读:127 评论:0 查看评论

21 RadioGroup ListFragment

$
0
0
  • 结构
    这里写图片描述

MainActivity.java

package com.qf.day21_radiogroupfragment_demo3;

import java.util.ArrayList;
import java.util.List;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;

public class MainActivity extends FragmentActivity {

    private RadioGroup rgMain;

    //Fragment数据源
    private List<Fragment> list = new ArrayList<Fragment>();

    private RadioButton[] rbs;

    private String[] titles={"news","happy","dz","cj"};

    private int currentIndex =0;//当前展示的Fragment

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        rgMain = (RadioGroup) findViewById(R.id.rg_main);

        initData();
        initTab();
    }

    //初始化标签
    private void initTab(){
        //改变标签内容
        rbs = new RadioButton[rgMain.getChildCount()];
        for(int i=0;i<rgMain.getChildCount();i++){
            rbs[i] = (RadioButton) rgMain.getChildAt(i);
            rbs[i].setText(titles[i]);
        }

        //点击按钮进行替换
        rgMain.setOnCheckedChangeListener(new OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                // TODO Auto-generated method stub
                for(int i=0;i<rgMain.getChildCount();i++){
                    if(rbs[i].getId() == checkedId){//当前按钮被点击
                        //开始替换
                        //replaceFragment(i);
                        switchFragment(i);
                    }

                }
            }
        });

    }
    //replace  缺点  影响性能
    public void replaceFragment(int index){
        MyFragment myFragment = MyFragment.getInstance(index+1);
        getSupportFragmentManager().
        beginTransaction().
        replace(R.id.layout_content_id, list.get(index)).commit();

    }

    //替换   使用 show() 和hide() 方法  减少性能开销
    public void  switchFragment(int targetIndex){

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

        //点击的Fragment(目标)
        Fragment targetFragment = list.get(targetIndex);
        //当前的Fragment
        Fragment currentFragment = list.get(currentIndex);

        //点击的 按钮对象的Fragment 存在    show()展示出来  隐藏当前的Fragment
        if(!targetFragment.isAdded()){
            transaction.add(R.id.layout_content_id, targetFragment).hide(currentFragment).commit();
        }else{
            transaction.show(targetFragment).hide(currentFragment).commit();
        }

        //当前展示的Fragment就是点击替换的Fragment
        currentIndex = targetIndex;

    }


    //初始化数据
    private void initData(){
        for(int i=0;i<rgMain.getChildCount();i++){
            MyFragment myFragment = MyFragment.getInstance(i+1);
            list.add(myFragment);
        }
        //程序运行  默认展示第一个Fragment
        getSupportFragmentManager().
        beginTransaction().
        add(R.id.layout_content_id, list.get(0)).commit();
    }


}

MyFragment.java

package com.qf.day21_radiogroupfragment_demo3;

import java.util.ArrayList;
import java.util.List;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;

public class MainActivity extends FragmentActivity {

    private RadioGroup rgMain;

    //Fragment数据源
    private List<Fragment> list = new ArrayList<Fragment>();

    private RadioButton[] rbs;

    private String[] titles={"news","happy","dz","cj"};

    private int currentIndex =0;//当前展示的Fragment

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        rgMain = (RadioGroup) findViewById(R.id.rg_main);

        initData();
        initTab();
    }

    //初始化标签
    private void initTab(){
        //改变标签内容
        rbs = new RadioButton[rgMain.getChildCount()];
        for(int i=0;i<rgMain.getChildCount();i++){
            rbs[i] = (RadioButton) rgMain.getChildAt(i);
            rbs[i].setText(titles[i]);
        }

        //点击按钮进行替换
        rgMain.setOnCheckedChangeListener(new OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                // TODO Auto-generated method stub
                for(int i=0;i<rgMain.getChildCount();i++){
                    if(rbs[i].getId() == checkedId){//当前按钮被点击
                        //开始替换
                        //replaceFragment(i);
                        switchFragment(i);
                    }

                }
            }
        });

    }
    //replace  缺点  影响性能
    public void replaceFragment(int index){
        MyFragment myFragment = MyFragment.getInstance(index+1);
        getSupportFragmentManager().
        beginTransaction().
        replace(R.id.layout_content_id, list.get(index)).commit();

    }

    //替换   使用 show() 和hide() 方法  减少性能开销
    public void  switchFragment(int targetIndex){

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

        //点击的Fragment(目标)
        Fragment targetFragment = list.get(targetIndex);
        //当前的Fragment
        Fragment currentFragment = list.get(currentIndex);

        //点击的 按钮对象的Fragment 存在    show()展示出来  隐藏当前的Fragment
        if(!targetFragment.isAdded()){
            transaction.add(R.id.layout_content_id, targetFragment).hide(currentFragment).commit();
        }else{
            transaction.show(targetFragment).hide(currentFragment).commit();
        }

        //当前展示的Fragment就是点击替换的Fragment
        currentIndex = targetIndex;

    }


    //初始化数据
    private void initData(){
        for(int i=0;i<rgMain.getChildCount();i++){
            MyFragment myFragment = MyFragment.getInstance(i+1);
            list.add(myFragment);
        }
        //程序运行  默认展示第一个Fragment
        getSupportFragmentManager().
        beginTransaction().
        add(R.id.layout_content_id, list.get(0)).commit();
    }


}

selecte_main.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_checked="true" android:drawable="@android:drawable/ic_menu_add"></item>
    <item android:state_checked="false" android:drawable="@android:drawable/ic_menu_call"></item>

</selector>

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"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <RadioGroup
        android:id="@+id/rg_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <RadioButton
            android:id="@+id/rb1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:button="@null"
            android:drawableTop="@drawable/selecte_main"
            android:gravity="center"
            android:checked="true"
            android:text="新闻" />

        <RadioButton
             android:id="@+id/rb2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:button="@null"
            android:drawableTop="@drawable/selecte_main"
            android:gravity="center"
            android:text="娱乐" />

        <RadioButton
             android:id="@+id/rb3"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:button="@null"
            android:drawableTop="@drawable/selecte_main"
            android:gravity="center"
            android:text="体育" />

        <RadioButton
             android:id="@+id/rb4"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:button="@null"
            android:drawableTop="@drawable/selecte_main"
            android:gravity="center"
            android:text="财经" />
    </RadioGroup>

    <FrameLayout 
        android:id="@+id/layout_content_id"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ></FrameLayout>

</LinearLayout>

fragment_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView 
        android:id="@+id/tv_show"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#f00"
        android:text="AAA"
        />
    <ListView
        android:id="@android:id/list" 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ></ListView>


</LinearLayout>

item.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"
     >
    <ImageView 
        android:id="@+id/iv_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"
        />
    <TextView 
        android:id="@+id/title_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_item"
        android:text="name"
        />
    <TextView 
        android:id="@+id/content_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_item"
        android:text="aaa"
        android:layout_alignBottom="@id/iv_item"
        />

</RelativeLayout>
作者:qfanmingyiq 发表于2016/9/20 19:31:15 原文链接
阅读:110 评论:0 查看评论

Android的事件机制

$
0
0

Android的事件机制

一、理论概述

最基本的操作类型:

  • down 手指按下
  • move 手指在屏幕上移动
  • up 手指从屏幕上离开

触屏操作的顺序:down->move->move->…->up

对屏幕的任一操作,系统都会产生一个MotionEvent对象来对应这个对象。

注:点击和长按可以同时满足,如果只想满足长按,则让长按的监听返回true。点击和长按时可以move。

二、相关API

1、MotionEvent:触发事件

  • int ACTION_DOWN = 0 : 代表down
  • int ACTION_MOVE = 2 : 代表move
  • int ACTION_UP = 1 : 代表up
  • getAction() : 得到事件类型
  • getX() : 得到事件发生的X轴的坐标(相对于当前视图)
  • getRawX() : 得到事件发生的X轴的坐标(相对于屏幕左顶点)
  • getY() : 得到事件发生的Y轴的坐标(相对于当前视图)
  • getRawY() : 得到事件发生的Y轴的坐标(相对于屏幕左顶点)

2、Activity

  • boolean dispatchTouchEvent(MotionEvent event) : 分发事件
  • boolean onTouchEvent(MotionEvent event) : 处理事件的回调(当没有子View消费时才调用该方法)

3、View

  • boolean dispatchTouchEvent(MotionEvent event) : 分发事件(没有子view,用来决定是使用onTouchEvent还是setOnTouchListener)
  • boolean onTouchEvent(MotionEvent event) : 处理事件的回调方法
  • void setOnTouchListener(OnTouchListener listener) : 设置事件监听器
  • void setOnClickListener(OnClickListener l)
  • void setOnLongClickListener(OnClickListener l)
  • void setOnCreateContextMenuListener(OnCreateContextMenuListener l) 用于创建菜单监听

4、ViewGroup

  • boolean dispatchTouchEvent(MotionEvent event) : 分发事件
  • boolean onInterceptTouchEvent(MotionEvent event) : 拦截事件的回调方法

注:这里引入两个概念:处理和消费
只要调用了方法就叫做处理了;
只有返回了true才叫消费了;

事件对象被系统创建后,首先会调用对应Activity的dispatchTouchEvent()进行分发;

  • down在分发给视图对象的过程中要确定消费者(onTouchEvent()返回true),如果都返回false,那事件的消费者只能是activity了。

  • 后面的move和up都将分发给消费者(可能是视图对象,也可能是消费者)

  • 当前事件的消费者只是决定了下一个事件优先交给他处理

  • 每个事件都需要有一个消费者

例子:

MotionEventActivity.java

package com.cwenhui.motionevent;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.cwenhui.test.R;

/**
 * Created by cwenhui on 2016.02.23
 */
public class MotionEventActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_motionevent);

        findViewById(R.id.myImageview).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e("MotionEventActivity", "setOnTouchListener");
                return false;
            }
        });
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e("MotionEventActivity", "dispatchTouchEvent--"+ev.getAction());
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("MotionEventActivity", "onTouchEvent--"+event.getAction());
        return super.onTouchEvent(event);
    }
}

layout_motionevent.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:gravity="center"
              android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="motionEvent"/>

    <com.cwenhui.motionevent.MyImageView
        android:id="@+id/myImageview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/test"/>

</LinearLayout>

MyImageview.java

package com.cwenhui.motionevent;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ImageView;

/**
 * Created by cwenhui on 2016.02.23
 */
public class MyImageView extends ImageView {
    public MyImageView(Context context) {
        super(context);
    }

    public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.e("MyImageView", "MyImageView");
    }

    public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e("MyImageView", "dispatchTouchEvent--"+event.getAction());
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("MyImageView", "onTouchEvent--"+event.getAction());
        return super.onTouchEvent(event)/*true*/;
    }
}

运行分析:
运行结果

如果点击图片并滑动,则

09-20 03:20:37.674 25085-25085/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--0
09-20 03:20:37.674 25085-25085/com.cwenhui.test E/MyImageView: dispatchTouchEvent--0
09-20 03:20:37.674 25085-25085/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 03:20:37.674 25085-25085/com.cwenhui.test E/MyImageView: onTouchEvent--0
09-20 03:20:37.674 25085-25085/com.cwenhui.test E/MotionEventActivity: onTouchEvent--0
09-20 03:20:37.708 25085-25085/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 03:20:37.708 25085-25085/com.cwenhui.test E/MotionEventActivity: onTouchEvent--2
09-20 03:20:37.724 25085-25085/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 03:20:37.724 25085-25085/com.cwenhui.test E/MotionEventActivity: onTouchEvent--2
09-20 03:20:37.741 25085-25085/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 03:20:37.741 25085-25085/com.cwenhui.test E/MotionEventActivity: onTouchEvent--2
09-20 03:20:37.758 25085-25085/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 03:20:37.758 25085-25085/com.cwenhui.test E/MotionEventActivity: onTouchEvent--2
09-20 03:20:37.774 25085-25085/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 03:20:37.774 25085-25085/com.cwenhui.test E/MotionEventActivity: onTouchEvent--2
09-20 03:20:37.802 25085-25085/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--1
09-20 03:20:37.802 25085-25085/com.cwenhui.test E/MotionEventActivity: onTouchEvent--1

如果将MyImageview.java中的onTouchEvent(MotionEvent event)返回值改为true,则

09-20 03:22:16.122 2077-2077/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--0
09-20 03:22:16.122 2077-2077/com.cwenhui.test E/MyImageView: dispatchTouchEvent--0
09-20 03:22:16.122 2077-2077/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 03:22:16.122 2077-2077/com.cwenhui.test E/MyImageView: onTouchEvent--0
09-20 03:22:16.174 2077-2077/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 03:22:16.174 2077-2077/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 03:22:16.174 2077-2077/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 03:22:16.174 2077-2077/com.cwenhui.test E/MyImageView: onTouchEvent--2
09-20 03:22:16.191 2077-2077/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 03:22:16.191 2077-2077/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 03:22:16.191 2077-2077/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 03:22:16.191 2077-2077/com.cwenhui.test E/MyImageView: onTouchEvent--2
09-20 03:22:16.208 2077-2077/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 03:22:16.208 2077-2077/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 03:22:16.208 2077-2077/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 03:22:16.208 2077-2077/com.cwenhui.test E/MyImageView: onTouchEvent--2
09-20 03:22:16.224 2077-2077/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 03:22:16.224 2077-2077/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 03:22:16.224 2077-2077/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 03:22:16.224 2077-2077/com.cwenhui.test E/MyImageView: onTouchEvent--2
09-20 03:22:16.279 2077-2077/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--1
09-20 03:22:16.279 2077-2077/com.cwenhui.test E/MyImageView: dispatchTouchEvent--1
09-20 03:22:16.279 2077-2077/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 03:22:16.279 2077-2077/com.cwenhui.test E/MyImageView: onTouchEvent--1

如果此时MotionEventActivity中的OnTouchListener的onTouch()方法返回true,如下:

findViewById(R.id.myImageview).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e("MotionEventActivity", "setOnTouchListener--"+event.getAction());
                return true;
            }
        });

运行结果如下:

09-20 15:01:53.068 3767-3767/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--0
09-20 15:01:53.068 3767-3767/com.cwenhui.test E/MyImageView: dispatchTouchEvent--0
09-20 15:01:53.068 3767-3767/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 15:01:53.105 3767-3767/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 15:01:53.105 3767-3767/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 15:01:53.105 3767-3767/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 15:01:53.122 3767-3767/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 15:01:53.122 3767-3767/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 15:01:53.122 3767-3767/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 15:01:53.138 3767-3767/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 15:01:53.139 3767-3767/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 15:01:53.139 3767-3767/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 15:01:53.155 3767-3767/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 15:01:53.155 3767-3767/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 15:01:53.155 3767-3767/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 15:01:53.172 3767-3767/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 15:01:53.172 3767-3767/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 15:01:53.172 3767-3767/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 15:01:53.189 3767-3767/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 15:01:53.189 3767-3767/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 15:01:53.189 3767-3767/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 15:01:53.237 3767-3767/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 15:01:53.237 3767-3767/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 15:01:53.237 3767-3767/com.cwenhui.test E/MotionEventActivity: setOnTouchListener
09-20 15:01:53.237 3767-3767/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--1
09-20 15:01:53.237 3767-3767/com.cwenhui.test E/MyImageView: dispatchTouchEvent--1
09-20 15:01:53.237 3767-3767/com.cwenhui.test E/MotionEventActivity: setOnTouchListener

可见,没有执行MyImageview的onTouchEvent(MotionEvent event)方法了。

如果改成手指按下时返回true,即

findViewById(R.id.myImageview).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e("MotionEventActivity", "setOnTouchListener--"+event.getAction());
//                return false;
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    return true;
                }

                return false;
            }
        });

结果为:

09-20 16:30:48.573 16591-16591/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--0
09-20 16:30:48.573 16591-16591/com.cwenhui.test E/MyImageView: dispatchTouchEvent--0
09-20 16:30:48.573 16591-16591/com.cwenhui.test E/MotionEventActivity: setOnTouchListener--0
09-20 16:30:48.605 16591-16591/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 16:30:48.605 16591-16591/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 16:30:48.605 16591-16591/com.cwenhui.test E/MotionEventActivity: setOnTouchListener--2
09-20 16:30:48.605 16591-16591/com.cwenhui.test E/MyImageView: onTouchEvent--2
09-20 16:30:48.622 16591-16591/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 16:30:48.622 16591-16591/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 16:30:48.622 16591-16591/com.cwenhui.test E/MotionEventActivity: setOnTouchListener--2
09-20 16:30:48.622 16591-16591/com.cwenhui.test E/MyImageView: onTouchEvent--2
09-20 16:30:48.638 16591-16591/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 16:30:48.639 16591-16591/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 16:30:48.639 16591-16591/com.cwenhui.test E/MotionEventActivity: setOnTouchListener--2
09-20 16:30:48.639 16591-16591/com.cwenhui.test E/MyImageView: onTouchEvent--2
09-20 16:30:48.655 16591-16591/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 16:30:48.655 16591-16591/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 16:30:48.655 16591-16591/com.cwenhui.test E/MotionEventActivity: setOnTouchListener--2
09-20 16:30:48.655 16591-16591/com.cwenhui.test E/MyImageView: onTouchEvent--2
09-20 16:30:48.672 16591-16591/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 16:30:48.672 16591-16591/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 16:30:48.672 16591-16591/com.cwenhui.test E/MotionEventActivity: setOnTouchListener--2
09-20 16:30:48.672 16591-16591/com.cwenhui.test E/MyImageView: onTouchEvent--2
09-20 16:30:48.689 16591-16591/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--2
09-20 16:30:48.689 16591-16591/com.cwenhui.test E/MyImageView: dispatchTouchEvent--2
09-20 16:30:48.689 16591-16591/com.cwenhui.test E/MotionEventActivity: setOnTouchListener--2
09-20 16:30:48.689 16591-16591/com.cwenhui.test E/MyImageView: onTouchEvent--2
09-20 16:30:48.717 16591-16591/com.cwenhui.test E/MotionEventActivity: dispatchTouchEvent--1
09-20 16:30:48.717 16591-16591/com.cwenhui.test E/MyImageView: dispatchTouchEvent--1
09-20 16:30:48.718 16591-16591/com.cwenhui.test E/MotionEventActivity: setOnTouchListener--1
09-20 16:30:48.718 16591-16591/com.cwenhui.test E/MyImageView: onTouchEvent--1

可见down的时候,事件对象是给OnTouchListener消费了;
move和up时,事件对象给OnTouch消费了。

作者:u014038534 发表于2016/9/20 20:33:10 原文链接
阅读:159 评论:0 查看评论

自定义ContentProvider

$
0
0

当多个应用程序共享同一数据时,我们可以给这个数据定义一个URI,然后把这个URI以接口的形式暴漏出去,之后其他应用程序对此数据进行增删改查时,只需要从当前上下文对象获得一个ContentResolver(内容解析器)传入相应的URI就可以了。数据库支持直接操作db文件,问什么还要用内容提供人?这是出于安全的考虑,内容提供者可以根据自己的意愿选择性的提供数据给客户端使用。

(一)、操作步骤:

1、编写一个类,让其继承自ContentProvider类;

2、实现ContentProvider类中所有的抽象方法;需要实现:onCreate() 、getType() 、query() 、insert() 、update()、delete() 等方法。

3、定义ContentProvider的Uri。这个Uri是ContentResolver对象执行CRUD操作时重要的参数;

4、使用UriMatcher对象映射Uri返回代码;

5、在AndroidMainfest.xml文件中使用<provider>标签注册ContentProvider。

备注:

ContentProvider暴露出来的数据和方法并不是给自身调用的,而是给其他应用程序来调用。其他应用程序通过其ContentResolver对象调用的query() 、insert() 、update()、delete() 等方法就是我们在这里暴露出来的ContentProvider类中的重写后的query() 、insert() 、update()、delete() 方法。


(二)、ContentProvider类中的六个抽象方法:

1、boolean  onCreate()

2、Uri  insert(Uri  uri, ContentValues  values)

3、int  delete(Uri  uri, String selection, String[]  selectionArgs)

4、int  update(Uri  uri, ContentValues values, String  selection, String[]  selectionArgs)

5、Cursor  query(Uri  uri, String[]  projection, String  selection, String[]  selectionArgs, String  sortOrder)

6、String  getType(Uri  uri)


(三)、ContentProvider类中六个抽象方法的说明:

1、onCreate() 初始化provider

2、query() 返回数据给调用者

3、insert() 插入新数据到ContentProvider

4、update() 更新ContentProvider已经存在的数据

5、delete() 从ContentProvider中删除数据

6、getType() 返回ContentProvider数据的Mime类型


(四)、在清单文件中声明注册ContentProvider:

<provider android:name=".MyWordsProvider"
android:authorities="com.steven.wordscontentprovider"
android:exported="true" />

//android:name  属性的值是:ContentProvider类的子类的完整路径;
//android:authorities 属性的值是:content:URI中authority部分。一般就是将name属性的值全小写。
//android:exported 属性是否允许其他应用调用。如果是false,则该ContentProvider不允许其他应用调用。

(五)、UriMatcher:
继承ContentProvider类后发现,ContentProvider类中只有一个onCreate()生命周期方法——当其他应用程序通过ContentResolver第一次访问ContentProvider时,onCreate()会被回调。
其他应用在通过ContentResolver对象执行CRUD操作时,都需要一个重要的参数Uri。为了能顺利提供这个Uri参数,Android系统提供了一个UriMatcher工具类。

UriMatcher工具类提供了两个方法:
1、void addURI(String authority , String path , int code) : 该方法用于向UriMatcher对象注册Uri。其中authority和path是Uri中的重要部分。而code代表该Uri对应的标示符。
2、int match(Uri uri) : 根据注册的Uri来判断指定的Uri对应的标示符。如果找不到匹配的标示符,该方法返回-1。
 private static UriMatcher matcher = null;
static {

// 定义一个Uri匹配器。将UriMatcher.NO_MATCH,即-1作为参数。
matcher = new UriMatcher(UriMatcher.NO_MATCH);
// 定义一组匹配规则
matcher.addURI(AUTHORITY, "words", 1);
matcher.addURI(AUTHORITY, "newwords", 2);
}
备注:
ContentProvider是单例模式的,当多个应用程序通过使用ContentResolver 来操作使用ContentProvider 提供的数据时,ContentResolver 调用的数据操作会委托给同一个ContentProvider 来处理。这样就能保证数据的一致性。

(六)下面通过一个实例来讲解自定义ContentProvider,需要创建两个工程,一个工程用来自定义ContentProvider,然后把uri在程序清单中暴漏出去,那么此工程就相当于一个服务器端,可以供其他的工程来访问其数据;另外一个工程相当于客户端,通过使用ContentResolver 来操作使用ContentProvider 提供的数据。在测试时要先启动服务端,再启动客服端。

1,自定义ContentProvider的工程:

①创建一个继承ContentProvider类:

package com.qf.mycontentprovide;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.annotation.Nullable;

/**
 * 多个应用共用同一个数据库,数据库就是一个db文件
 * 数据库支持直接操作db文件,为什么还要用内容提供者?
 * 答:出于安全的考虑,内容提供者可以根据的自己的意愿选择性的提供数据给客户端使用。
 *
 * 服务器和客户端,安卓有jdbc的api,意味着安卓是可以直接访问数据库的,
 * 但是实际中我们使用接口访问远程服务器,这个接口提供给数据给我们的同时也是对远程数据库提供了保护,
 * 可以选择性的返回数据给客户端
 */
public class MyContentProvider extends ContentProvider{

    private SQLiteDatabase database;
    private UriMatcher uriMatcher;

    /**
     * 安装之后会执行,以后app启动不会执行
     * 并且应用没有启动的时候,其他应用也是可以访问自己提供的数据的
     * 和数据库的openhelper中的oncreate不一样,数据库是卸载再安装才会执行,
     * 而MyContentProvider只要重新安装就会执行
     */
    @Override
    public boolean onCreate() {
        DbHelper dbHelper = new DbHelper(getContext());
        database = dbHelper.getReadableDatabase();

        //获取到一个uri匹配对象
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        /**
         *  addURI是把authority和path进行拼接
         *  code是给path设置了一个id
         */
        uriMatcher.addURI("com.qf.mycontentprovide.MyContentProvider","person",1);
        uriMatcher.addURI("com.qf.mycontentprovide.MyContentProvider","teacher",2);
        return true;
    }

    /**
     * 操作数据库时,因为数据库有多张表,需要判断操作那张表,
     * 这时addURI的参数code就有了用武之地
     */
    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Cursor cursor = null;
        int match = uriMatcher.match(uri);
        switch (match){
            //person表
            case 1:
                cursor = database.query("person", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            //teacher表
            case 2:
                cursor = database.query("teacher", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }

    /**
     * ContentValues里封装了一个HashMap<String, Object> mValues;
     * 和普通的map的区别:键只能为string
     * @param uri  客户端传过来的uri路径
     * @param values  客户端封装的数据传过来
     * @return
     */
    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        int match = uriMatcher.match(uri);
        long num = 0; //插入数据受影响的行数
        switch (match){
            case 1:
                num = database.insert("person", null, values);
                break;
            case 2:
                num = database.insert("teacher",null,values);
                break;
            default:
                break;
        }
        //把受影响的行数拼接到uri后面
        Uri uri1 = ContentUris.withAppendedId(uri, num);
        return uri1;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int match = uriMatcher.match(uri);
        int num = 0;
        switch (match){
            case 1:
                num = database.delete("person", selection, selectionArgs);
                break;
            case 2:
                num = database.delete("person", selection, selectionArgs);
                break;
        }
        return num;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        int match = uriMatcher.match(uri);
        int num = 0;
        switch (match){
            case 1:
                num = database.update("person", values, selection, selectionArgs);
                break;
            case 2:
                num = database.update("teacher",values,selection,selectionArgs);
                break;
        }
        return num;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

}


②创建一个数据库操作类,用来向外提供数据:

package com.qf.mycontentprovide;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * Created by Administrator on 2016/9/20.
 * 支持内容提供者向外提供数据的数据库
 */
public class DbHelper extends SQLiteOpenHelper {

    public DbHelper(Context context) {
        super(context, "Person.db", null, 2);
    }

    /**
     * 创建数据库时创建一张person表,并给表初始化一条数据
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table person(_id integer primary key autoincrement,name text,pass text)");
        db.execSQL("insert into person(name,pass)values('张飞','123')");
    }

    /**
     * 更新数据库时,再创建一张teacher表,并初始化一条数据
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("create table teacher(_id integer primary key autoincrement,name text,pass text)");
        db.execSQL("insert into person(name,pass)values('老张','12334')");
    }
}

③在清单文件中暴漏此内容提供者:

<span style="color:#464646;"><?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.qf.mycontentprovide">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- authorities:向外提供数据的接口,即uri,一般为包名+类名
             exported:是否支持其他应用访问自己的数据,设置为true  -->
        </span><span style="color:#ff0000;"><provider
            android:authorities="com.qf.mycontentprovide.MyContentProvider"
            android:name=".MyContentProvider"
            android:exported="true">
        </provider></span><span style="color:#464646;">
    </application>

</manifest></span>
由于MainActivity和activity_main.xml中不需要写代码,所以就不再给出。


2.再创建一个去共享内容提供者中的数据的工程:

①布局文件activity_main.xml中的代码:

<?xml version="1.0" encoding="utf-8"?>
<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" android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.example.client.MainActivity">

    <Button
        android:id="@+id/query_person"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="查询person表" />
    <Button
        android:id="@+id/query_teacher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="查询teacher表" />

    <Button
        android:id="@+id/insert_person"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="插入数据到person表" />
    <Button
        android:id="@+id/insert_teacher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="插入数据到teacher表" />

    <Button
        android:id="@+id/delete_person"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="删除person表中的数据" />
    <Button
        android:id="@+id/delete_teacher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="删除teacher表中的数据" />

    <Button
        android:id="@+id/updata_person"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="修改person表中的数据" />
    <Button
        android:id="@+id/updata_teacher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="修改teacher表中的数据" />

</LinearLayout>


②MainActivity中的代码:

package com.example.client;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private ContentResolver contentResolver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //初始化控件,并给控件设置监听
        findViewById(R.id.query_person).setOnClickListener(this);
        findViewById(R.id.query_teacher).setOnClickListener(this);
        findViewById(R.id.insert_person).setOnClickListener(this);
        findViewById(R.id.insert_teacher).setOnClickListener(this);
        findViewById(R.id.delete_person).setOnClickListener(this);
        findViewById(R.id.delete_teacher).setOnClickListener(this);
        findViewById(R.id.updata_person).setOnClickListener(this);
        findViewById(R.id.updata_teacher).setOnClickListener(this);

        //获得ContentResolver对象来操作数据库
        contentResolver = getContentResolver();
    }

    @Override
    public void onClick(View v) {
        //person表的uri
        Uri personUri = Uri.parse("content://com.qf.mycontentprovide.MyContentProvider/person");
        //teacher表的uri
        Uri teacherUri = Uri.parse("content://com.qf.mycontentprovide.MyContentProvider/teacher");
        switch (v.getId()){
            //查询person表
            case R.id.query_person:
                Cursor cursor = contentResolver.query(personUri, new String[]{"name", "pass"}, null, null, null,null);
                while(cursor.moveToNext()){
                    String name = cursor.getString(cursor.getColumnIndex("name"));
                    String pass = cursor.getString(cursor.getColumnIndex("pass"));
                    Log.e("name",name);
                    Log.e("pass",pass);
                }
                break;

            //查询teacher表
            case R.id.query_teacher:
                Cursor cursor2 = contentResolver.query(teacherUri, new String[]{"name", "pass"}, null, null, null,null);
                while(cursor2.moveToNext()){
                    String name = cursor2.getString(cursor2.getColumnIndex("name"));
                    String pass = cursor2.getString(cursor2.getColumnIndex("pass"));
                    Log.e("name",name);
                    Log.e("pass",pass);
                }
                break;

            case R.id.insert_person:
                ContentValues values = new ContentValues();
                values.put("name","关羽");
                values.put("pass","456");
                Uri insert = contentResolver.insert(personUri, values);
                long num = ContentUris.parseId(insert);
                Log.e("num",num + "");
                break;

            case R.id.insert_teacher:
                ContentValues values2 = new ContentValues();
                values2.put("name","曹操");
                values2.put("pass","321");
                Uri insert2 = contentResolver.insert(teacherUri, values2);
                long num2 = ContentUris.parseId(insert2);
                Log.e("num",num2 + "");
                break;

            case R.id.delete_person:
                int num3 = contentResolver.delete(personUri, "name = ?", new String[]{"张飞"});
                Log.e("num3",num3 + "");
                break;

            case R.id.delete_teacher:
                int num4 = contentResolver.delete(teacherUri, "name = ?", new String[]{"老张"});
                Log.e("num4",num4 + "");
                break;

            case R.id.updata_person:
                ContentValues values3 = new ContentValues();
                values3.put("name","关羽");
                values3.put("pass","789");
                int num5 = contentResolver.update(personUri, values3, null, null);
                Log.e("num5",num5 + "");
                break;

            case R.id.updata_teacher:
                ContentValues values4 = new ContentValues();
                values4.put("name","曹操");
                values4.put("pass","246");
                int num6 = contentResolver.update(personUri, values4, null, null);
                Log.e("num6",num6 + "");
                break;
        }
    }
}









作者:ljw124213 发表于2016/9/20 20:43:16 原文链接
阅读:141 评论:0 查看评论

Android 必知必会 - RadioGroup 和 ViewPager 联动

$
0
0

如果移动端访问不佳或需要更好的阅读体验,欢迎使用 ==> Github 版

使用 RadioGroup 和 ViewPager 实现更加可定制的效果。

背景

昨天设计图刚出一点,写了《Android 必知必会 - 动态切换着色模式和全屏模式》,记录了动态修改页面显示模式的方式。今天又有新图,不过设计师只考虑 iOS 平台的设计,拿到设计图发现 TabLayout + ViewPager 的套路实现起来很麻烦,考虑了下,为了方便,决定使用 RadioGroup + ViewPager 来实现,之所以使用 RadioGroup ,是因为它内部多个 RadioButton 的状态是互斥的,也就是只有一个是选中状态,不需要我们进行多余的处理。总体来说比较简单,就是细节略多了点。

主要知识点:

  • 自定义 RadioButton 样式 : selector + shape
  • 自定义 RadioButton 文字样式 : selector
  • ViewPager + Fragment 及其适配器
  • RadioGroup 状态监听
  • ViewPager 页面切换监听

实现

先看效果图:

思路

UI:

  • 顶部是 RadioGroup ,内部包含两个 RadioButton
    • RadioButton 需要自定义背景和文字
  • 中间是 ViewPager

联动事件:

  • ViewPager
    • 需要为 ViewPager 写适配器,以配合 Fragment
    • 使用 addOnPageChangeListener() 为其添加监听页面变动的事件
    • onPageSelected(int position) 方法中修改 RadioGroupRadioButton 的选中状态
  • RadioGroup
    • 设置 setOnCheckedChangeListener 监听,并在其中修改 ViewPager 的状态

实现 UI

<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"
    >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:background="@color/title_bar">

        <RadioGroup
            android:id="@+id/main_top_rg"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:orientation="horizontal">

            <RadioButton
                android:id="@+id/top_rg_a"
                android:layout_width="76dp"
                android:layout_height="29dp"
                android:background="@drawable/top_r_bg"
                android:button="@null"
                android:checked="true"
                android:gravity="center"
                android:text="@string/main_tab1"
                android:textColor="@drawable/top_r_text"
                android:textColorHighlight="@color/title_bar"
                android:textSize="15sp"/>

            <RadioButton
                android:id="@+id/top_rg_b"
                android:layout_width="76dp"
                android:layout_height="29dp"
                android:background="@drawable/top_r_bg2"
                android:button="@null"
                android:gravity="center"
                android:layout_marginLeft="-1dp"
                android:text="@string/main_tab2"
                android:textColor="@drawable/top_r_text"
                android:textColorHighlight="@color/title_bar"
                android:textSize="15sp"/>
        </RadioGroup>

        <ImageView
            android:id="@+id/main_top_right"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:paddingLeft="@dimen/left_padding"
            android:paddingRight="@dimen/right_padding"
            android:src="@drawable/main_search"
            />
    </RelativeLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/main_viewpager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        />

</LinearLayout>

重点在 RadioButton 的几个属性:

  • android:button="@null" 隐藏 RadioButton 默认的图标
  • android:background="@drawable/top_r_bg" 设置背景,实际上是一个 selector
  • android:textColor="@drawable/top_r_text" 设置文字颜色,它也是一个 selector
  • 对于第二个 RadioButtonandroid:layout_marginLeft="-1dp" 和描边宽度一样,防止出现间隙

下面把 xml 代码放在一起看,它们都放在 drawable 目录下:

<!-- drawable/top_r_bg -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@drawable/top_r_bg_a"
        android:state_checked="false"/>
    <item
        android:drawable="@drawable/top_r_bg_b"
        android:state_checked="true"/>
</selector>

<!-- drawable/top_r_bg_a -->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners
        android:bottomLeftRadius="2dp"
        android:bottomRightRadius="0dp"
        android:topLeftRadius="2dp"
        android:topRightRadius="0dp"
        />
    <stroke
        android:width="1dp"
        android:color="@color/white"/>
    <solid android:color="@color/transparent"/>
</shape>

<!-- drawable/top_r_bg_b -->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners
        android:bottomLeftRadius="2dp"
        android:topLeftRadius="2dp"/>
    <solid android:color="@color/white"/>
</shape>

<!-- drawable/top_r_text -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/white" android:state_checked="false"/>
    <item android:color="@color/title_bar" android:state_checked="true"/>
</selector>

上面只贴出了左边按钮的样式,右边的类似,不再张贴。UI 到这里已经完成,下面看事件:

实现联动事件

private void init() {
        List<Fragment> fragments = new ArrayList<>();
        fragments.add(new FragmentMain01A());
        fragments.add(new FragmentMain01B());

        NotePagerAdapter pagerAdapter = new NotePagerAdapter(getFragmentManager(), fragments);
        mainViewpager.setAdapter(pagerAdapter);
  /**
   * 为 Viewpager 设置页面切换监听,当页面切换完成被选中时,我们同步 RadioButton 的状态
   **/
        mainViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                if (position == 0) radioButtonA.setChecked(true);
                else radioButtonB.setChecked(true);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
  /**
   * 为 RadioGroup 设置选中变化事件监听,当 RadioButton 状态变化,我们同步 Viewpager 的选中页面
   **/
        mRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                if (checkedId == radioButtonA.getId()) mainViewpager.setCurrentItem(0);
                else if (checkedId == radioButtonB.getId()) mainViewpager.setCurrentItem(1);
            }
        });

        //设置默认选中页
        mainViewpager.setCurrentItem(0);
    }

对了,还有 NotePagerAdapter 的代码,这个比较简单,是 ViewPagerFragment 的适配器:

public class NotePagerAdapter extends FragmentStatePagerAdapter {
    private List<Fragment> mFragments;

    public  NotePagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);
        mFragments = fragments;
    }

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

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

}

总结

注意,以上代码使用的是 android.support.v4 包的类,理论上不使用 V4 包是没问题的。

看完整体的代码发现并没什么难点,纯属基础知识的叠加,主要是 UI 控件上的细节需要处理的比较多。

再者,如果顶部的控件超过2个,还需要更多的 xml 文件,顶部的几个控件是可以封装成一个自定义 View 的,甚至加上中间的 ViewPager 一起封装。等待我后面的成果吧。如果有什么疑问或建议,可以通过文末的联系方式和我交流。

最后,纪念一下自己今天正式成为 CSDN博客专家 ,祝愿广大程序员都能在编程的路上有所成就!

PS:你可以通过下面的方式和我联系

作者:ys743276112 发表于2016/9/20 20:47:27 原文链接
阅读:417 评论:0 查看评论

Android——滑动监听SwipeRefreshLayout+瀑布流Recycl+Butter自动生成

$
0
0

Android——滑动监听SwipeRefreshLayout+瀑布流Recycl+Butter自动生成



package c.example.jreduch09;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;

import c.example.jreduch09.util.RefreshLayout;

public class Main4RefreshlayoutActivity extends AppCompatActivity {
    private RefreshLayout refreshLayout;
    private SwipeRefreshLayout srl;
private List list;
    private View v;
    private ArrayAdapter aa;
    private ListView lv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main4);
        lv=(ListView)findViewById(R.id.lv);
       // refreshLayout= (RefreshLayout)findViewById(R.id.AArticleSwipeRefresh);
        srl=(SwipeRefreshLayout)findViewById(R.id.srl);
        list=new ArrayList();
        aa=new ArrayAdapter(this,android.R.layout.simple_list_item_1,list);
        lv.setAdapter(aa);

//refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
//    @Override
//    public void onRefresh() {
//        list.clear();
//        for (int i=0;i<20;i++){
//            list.add("di"+i+"xiaoxi");
//
//        }
//        aa.notifyDataSetChanged();
//        refreshLayout.setRefreshing(false);
//    }
//});
//上拉加载更多
//       refreshLayout.setOnLoadListener(new RefreshLayout.OnLoadListener() {
//           @Override
//           public void onLoad() {
//               for (int i=0;i<20;i++){
//                   list.add("新增加22di22"+i+"xiaoxi");
//
//               }
//               aa.notifyDataSetChanged();
//               refreshLayout.setLoading(false);
//           }
//       });



        srl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                list.clear();
                for (int a=0;a<20;a++){
                    list.add("刷新"+a+"数据");
                }
                aa.notifyDataSetChanged();
                srl.setRefreshing(false);
            }
        });



        lv.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView absListView, int i) {

            }
            @Override
            public void onScroll(AbsListView absListView, int i, int i1, int i2) {
                   if (i2==0){
                       return;
                    }
                int count =lv.getFooterViewsCount();
                   if(count==0){
                       if (i+i1==i2){
                           if(lv.getFooterViewsCount()==1){
                               return;
                           }
                           lv.addFooterView(v);
                           aa.notifyDataSetChanged();
                           new Test().execute();
                       }
                   }
            }
        });
    }
    public class  Test extends AsyncTask<Void,Void,Void>{
        @Override
        protected Void doInBackground(Void... voids) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return null;
        }
        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            for (int a=0;a<20;a++){
                list.add("新增"+a+"数据");
            }
            lv.removeFooterView(v);
            aa.notifyDataSetChanged();
        }
    }
}

<?xml version="1.0" encoding="utf-8"?>
<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=".Main4RefreshlayoutActivity">
<!--<c.example.jreduch09.util.RefreshLayout-->
    <!--android:id="@+id/AArticleSwipeRefresh"-->
    <!--android:layout_width="match_parent"-->
    <!--android:layout_height="match_parent">-->
    <!--<ListView-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="match_parent"-->
        <!--android:id="@+id/lv"-->
      <!-->-->
    <!--</ListView>-->
<!--</c.example.jreduch09.util.RefreshLayout>-->

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ListView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/lv"
            >
        </ListView></android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>

瀑布流Recycle

package c.example.jreduch10;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import butterknife.Bind;
import butterknife.ButterKnife;
import c.example.jreduch10.util.DividerItemDecoration;

public class RecyclerActivity extends AppCompatActivity {
private  List<Map> list;
    @Bind(R.id.rv)
    RecyclerView rv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler);
        ButterKnife.bind(this);
         list=new ArrayList<>();
        initdata();
     MyAdapter myAdapter=new MyAdapter(list);
        //瀑布流管理器
        StaggeredGridLayoutManager sgm=
                new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
        rv.setLayoutManager(sgm);
        rv.setAdapter(myAdapter);
        //设置分割线
        DividerItemDecoration itemDecoration=
                new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST);
        rv.addItemDecoration(itemDecoration);
    }
    public  void initdata(){
        HashMap map=new HashMap();
        map.put("img",R.mipmap.g);
         map.put("text","gggggg");
        list.add(map);
        map=new HashMap();
        map.put("img",R.mipmap.zyf);
        map.put("text","zyf");
        list.add(map);
        map=new HashMap();
        map.put("img",R.mipmap.zyfzyf);
        map.put("text","zyfzyf");
        list.add(map);
        map=new HashMap();
        map.put("img",R.mipmap.zyfzyfzyf);
        map.put("text","zyfzyfzyf");
        list.add(map);
        list.add(map);
        map=new HashMap();
        map.put("img",R.mipmap.zyf);
        map.put("text","zyf");
        list.add(map);
        map=new HashMap();
        map.put("img",R.mipmap.zyfzyf);
        map.put("text","zyfzyf");
        list.add(map);
        map=new HashMap();
        map.put("img",R.mipmap.zyfzyfzyf);
        map.put("text","zyfzyfzyf");
        list.add(map);
        map=new HashMap();
        map.put("img",R.mipmap.g);
        map.put("text","gggggg");
        list.add(map);
        map=new HashMap();
        map.put("img",R.mipmap.zyf);
        map.put("text","zyf");
        list.add(map);
        map=new HashMap();
        map.put("img",R.mipmap.zyfzyf);
        map.put("text","zyfzyf");
        list.add(map);
    }
    public class MyAdapter extends RecyclerView.Adapter<MyViewHolder>{
private List<Map> mapList;
        public MyAdapter(List<Map> mapList){
            this.mapList=mapList;
        }
        @Override
        public int getItemCount() {
            return mapList.size();
        }

        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

          View view=LayoutInflater.from(getBaseContext()).inflate(R.layout.recycler_stag_layout,parent,false);
           MyViewHolder viewHolder=new MyViewHolder(view);
            return viewHolder;
        }

        @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {
 Map map= mapList.get(position);
            holder.imageView.setImageResource((Integer) map.get("img"));
            holder.textView.setText(map.get("text").toString());
        }


    }
    public class MyViewHolder extends RecyclerView.ViewHolder{
        ImageView imageView;
        TextView textView;

        public MyViewHolder(View itemView) {
            super(itemView);
            imageView= (ImageView) itemView.findViewById(R.id.img);
            textView=(TextView)itemView.findViewById(R.id.tv1);
        }
    }
}

<?xml version="1.0" encoding="utf-8"?>
<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="c.example.jreduch10.RecyclerActivity">

    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/rv"
        ></android.support.v7.widget.RecyclerView>

</RelativeLayout>

package c.example.jreduch10.util;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

public class RecyclerViewItemListener
        implements RecyclerView.OnItemTouchListener {
    //定义Item单击监听器
    public interface OnItemClickListener{
        public void OnItemClick(View item, int adapterPosition);
    }
    //定义手势
    private GestureDetector mGestureDetector;
    private OnItemClickListener itemClickListener;

    public RecyclerViewItemListener(Context mContext,
                                    OnItemClickListener itemClickListener) {
        //初始化自定义单击监听器
        this.itemClickListener = itemClickListener;
        //初始手势
        this.mGestureDetector = new GestureDetector(mContext,new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                //必须返回true
                return true;
            }
        });
    }
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        //根据点击位置计算对应的Item子视图
        View cView = rv.findChildViewUnder(e.getX(),e.getY());
        if(cView!=null && itemClickListener!=null
                && mGestureDetector.onTouchEvent(e)){
            //根据子视图放回子视图在RecyclerView adapter中的位置
            int position =rv.getChildAdapterPosition(cView);
            itemClickListener.OnItemClick(cView,position);
            return true;
        }
        return false;
    }
    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
    }
    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    }
}




package c.example.jreduch10;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class ButterActivity extends AppCompatActivity {
    @Bind(R.id.tv1)
    TextView tv1;
    @Bind(R.id.tv2)
    TextView tv2;
    @Bind(R.id.tv3)
    TextView tv3;
    @Bind(R.id.tv4)
    TextView tv4;
    @Bind(R.id.tv5)
    TextView tv5;

    //@BindView(R.id.tv1)TextView tv1;
//    @BindView(R.id.tv2)TextView tv2;
//    @BindView(R.id.tv3)TextView tv3;
//    @BindView(R.id.tv4)TextView tv4;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_butter);
        ButterKnife.bind(this);
//        ButterKnife.bind(this);
        tv1.setText("1111");

    }

    @OnClick(R.id.tv1)
    public void onClick() {
        tv2.setText("22222");
    }

    @OnClick({R.id.tv4, R.id.tv5})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.tv4:
                tv4.setText("44444");
                break;
            case R.id.tv5:
                tv5.setText("55555");
                break;

        }
        tv3.setText("33333344444455555");
    }
}

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:orientation="vertical"

    tools:context="c.example.jreduch10.ButterActivity">
<TextView
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:id="@+id/tv1"
    />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:id="@+id/tv2"
        />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:id="@+id/tv3"
        />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:id="@+id/tv4"
        />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:id="@+id/tv5"
        />
</LinearLayout>





作者:zhangyufeng0126 发表于2016/9/20 20:51:08 原文链接
阅读:172 评论:0 查看评论

适配器模式 : 农村小伙娶乌克兰美女语言不通 翻译软件立功

$
0
0

不知道什么时候开始,总听到“ XXX 小伙娶乌克兰美女” 的新闻,比如 农村小伙娶乌克兰美女语言不通 翻译软件立功 等等,我仔细地看了几篇新闻,发现居然不是标题党,新闻里的乌克兰妹子长得真不错,上几张图:
这里写图片描述
这里写图片描述

看完这些新闻和照片,我心里有三个疑问;
1. 乌克兰真的美女很多吗?
2. 为什么乌克兰美女爱嫁给中国男人?
3. 翻译软件可以化腐朽为神奇,软件开发过程中是否可以参考呢?

经过我大量的研究,得出了答案:

1.乌克兰真的美女很多吗?

这里写图片描述

答:是的。
- 首先从世界地图可以看到,乌克兰地处东欧多个国家交界处,国内民族多达 110 个,各名族之间通婚比较多,久而久之导致混血美女比例比较高。
- 而且,乌克兰的气候环境也比较养人,一年到头冷多热少,阳光直射时间短,导致大多数女孩子皮肤白皙。
- 除此外,乌克兰姑娘特别注重外表,打扮的比较精致时尚。

这里写图片描述

2.为什么乌克兰美女爱嫁给中国男人?

答:除主观因素外,有两点客观因素很重要。
- 近些年乌克兰并不富裕,距离大家心中的资本主义发达国家还有段距离。一方面生产停滞,经济增长无力;另一方面,乌克兰还要面临战乱带来的货币贬值、外资流出、物价上涨等压力,财政“只出不进”,整个国家“干耗”外汇储备。所以许多乌克兰姑娘选择外嫁。
- 此外,由于文化、社会福利等原因,许多乌克兰男人有酗酒、懒散的习惯,而中国男人在国际上给人一种体贴、勤劳、顾家的形象,所以相较之下,中国男人是比较好的选择。
这里写图片描述
这里写图片描述

3.翻译软件可以化腐朽为神奇,软件开发过程中是否可以参考呢?

翻译软件把小伙的汉语转换成了乌克兰语,在软件开发过程中这就是一种“复用”!那有什么设计模式可以达到这种效果呢?

我们先来模拟实现下这个翻译过程:

a.首先定义一个小目标,就是可以跟妹子说乌克兰语,萨瓦迪卡爱米思油~

/**
 * description:目标:说乌克兰语
 * <br/>
 * author: shixinzhang
 * <br/>
 * data: 9/18/2016
 */
public interface Ukrainian {
    /**
     * 说乌克兰语,比如:Я люблю тебя 
     * @param string
     */
    void sayUkrainian(String string);
}

b.然而理想很丰满,现实很骨感,小伙只会川普:

/**
 * description:实际情况:只会中文
 * <br/>
 * author: shixinzhang
 * <br/>
 * data: 9/18/2016
 */
public class Chinese {
    /**
     * 说中文,比如:刘奶奶找牛奶奶买榴莲牛奶
     * @param string
     */
    void sayChinese(String string) {
        System.out.println("【中文版】 " + string);
    }
}

c.这时候翻译器上场了,化腐朽为神器,帮助小伙具有能说乌克兰语的功能:

/**
 * description:翻译
 * <br/>
 * author: shixinzhang
 * <br/>
 * data: 9/18/2016
 */
public class Translator implements Ukrainian {
    private Chinese mChinese;

    public Translator(Chinese chinese) {
        mChinese = chinese;
    }

    @Override
    public void sayUkrainian(String string) {
        //省略了复杂的语法翻译过程,想象一下
        mChinese.sayChinese(string);
    }
}

d.可以看到,翻译器持有一个只会中文小伙的引用,实现了说乌克兰语的接口,在需要说乌克兰语的时候,经过语法翻译最终调用小伙的说中文:

@Test
    public void testAdapterPattern(){
        Chinese me = new Chinese();
        Ukrainian ukrainianMan = new Translator(me);
        ukrainianMan.sayUkrainian("我爱你");
    }

e.翻译 + 川普小伙 = 乌克兰语达人,运行结果:
这里写图片描述

f.画一下上面这个过程的 UML 图:
这里写图片描述

  • 目标类,即能说乌克兰语,是一个接口;
  • 实际情况,即只能说汉语,是一个既成的、无法改变的类;
  • 中间人,即翻译软件,实现目标接口(乌克兰语),引用了实际情况(中国小伙),经过偷梁换柱,让中国小伙具有了新的功能
  • Client 客户端,乌克兰妹子,希望能和会乌克兰语的人沟通,由于翻译软件实现了乌克兰语接口,因此可以直接实例化一个翻译软件作为乌克兰语人。
@Test
    public void testAdapterPattern(){
        Chinese me = new Chinese();
        Ukrainian ukrainianMan = new Translator(me);
        ukrainianMan.sayUkrainian("我爱你");
    }

这就是适配器模式,又称包装模式

定义

将一个类的接口转换为客户希望的另一个接口。
适配器模式可以使原本不兼容的接口变得兼容,即能复用。

一个很形象的例子

这里写图片描述

适配器模式主要分为两种:类适配器和对象适配器

1.对象适配器,与被适配类是关联关系

上面举的例子就是适配器 。Adapter 中持有一个被适配类对象的引用,因此叫做对象适配器。
对象适配器的 UML 图和上述例子一致,所以就偷个懒不列出来了。

2.类适配器,与被适配类是继承关系

Adapter 通过继承被适配类,从而可以调用被适配类的方法。
举个栗子,类适配器下的翻译中介:

/**
 * description: 类适配器下的翻译中介
 * <br/>
 * author: shixinzhang
 * <br/>
 * data: 9/20/2016
 */
public class ClassTranslator extends Chinese implements Ukrainian {
    @Override
    public void sayUkrainian(String string) {
        sayChinese(string);
    }
}

采用类适配器模式的翻译软件,继承了被适配类 Chinese,实现了目标接口 Ukrainian,从而使得原本不能使用的 sayChinese(string) 方法可以被调用。

调用时:

    @Test
    public void testClassAdapterPattern(){
        Ukrainian ukrainianMan = new ClassTranslator();
        ukrainianMan.sayUkrainian("刘奶奶找牛奶奶买榴莲牛奶");
    }

这里写图片描述

对比一下对象适配器的代码:

/**
 * description: 翻译
 * <br/>
 * author: shixinzhang
 * <br/>
 * data: 9/18/2016
 */
public class Translator implements Ukrainian {
    private Chinese mChinese;

    public Translator(Chinese chinese) {
        mChinese = chinese;
    }

    @Override
    public void sayUkrainian(String string) {
        mChinese.sayChinese(string);
    }
}

可以看到,对象适配器支持传入一个被适配器对象,因此可以做到对多种被适配接口进行适配,而类适配器直接继承,无法动态修改,所以一般情况下对象适配器使用更广泛。

使用场景:就是想复用,不想多创建!

  1. 通常在软件开发后期或者维护期使用,因为这个接口可能已经投入使用,但是对新需求不太符合,我们希望尽可能复用原有接口,所以用适配器进行包装一下。
    .
  2. 或者一开始设计不合理,功能相似,由于参数或者名称等细小原因不能重用时,也可以考虑包装一下。

《大话设计模式》里看到的一段话很好

  • 事先设计统一接口
  • 问题初现及时重构(下策)
  • 无法改变只能适配(下下策)

后记

说起适配器 Adapter,最熟悉的就是 ListView 和 RecyclerVIew 的适配器了,本来准备下一篇就写 ListVIew 源码中的适配器模式,但考虑到 ListView 中还有观察者模式,所以下一步先总结观察者模式,然后再统一进行 ListView 源码解析

作者:u011240877 发表于2016/9/20 22:05:51 原文链接
阅读:165 评论:0 查看评论

Android 内存管理机制详解

$
0
0

  嵌入式设备的一个普遍特点是内存容量相对有限。当运行的程序超过一定数量时,或者涉及复杂的计算时,很可能出现内存不足,进而导致系统卡顿的现象。Android 系统也不例外,它同样面临着设备物理内存短缺的困境。对于已经启动过一次的Android程序,再一次启动所花的时间会明显减少。原因在于Android系统并不马上清理那些已经”淡出视野”的程序(比如你调用Activity.finish退出UI界面)。它们在一定的时间里仍然驻留在内存中。这样做的好处是明显的,即下一次启动不需要再为程序重新创建一个进程;坏处就是,加大了内存OOM的概率。

Linux内存监控机制(OOMKiller)

  Android是基于Linux的,而Linux底层内核有自己的内存监控机制,即OOMKiller。一旦发现系统的可用内存达到临界值,这个OOM的管理者就会自动跳出来清理内存。

OOMKiller有不同的策略和不同的处理手段。它的核心思想如下:

按照优先级顺序,从低到高逐步杀掉进程,回收内存。

优先级的设定策略主要考虑两个方面:一个是要考虑对系统的损害程度(例如系统的核心进程,优先级通常较高),另一方面也希望尽可能多地释放无用内存。一个合理的策略至少需要考虑以下几个因素:

  • 进程消耗的内存
  • 进程占用的CPU时间
  • oom_adj(OOM权重)

  内核所管理的进程都有一个衡量其oom权重的值,存储在/proc/< PID >/oom_adj中。根据这一权重值以及上面所提及的若干其他因素,系统会实时给每个进程评分,以决定OOM时应该杀死哪些进程。
这个值存储在/proc/< PID >/oom_score中。
oom_score分数越低的进程,被杀死的概率越小,或者说被杀死的时间越晚。
下面展示了PID为5912的NetworkManager进程的oom_adj 和oom_score,可以看到分数很低,说明此进程十分重要,一般不会被系统杀死。

oom_score

Android 内存管理机制

基于Linux内核OOM Killer的核心思想,Android 系统扩展出了自己的内存监控体系。因为Linux下的内存杀手需要等到系统资源”濒临绝境”的情况下才会产生效果,而Android则实现了自己的Killer.

Android 系统为此开发了一个专门的驱动,名为Low Memory Killer(LMK)。源码路径在内核工程的 drivers/staging/android/Lowmemorykiller.c中。
它的驱动加载函数如下:

static int __init lowmem_init(void)
{
    register_shrinker(&lowmem_shrinker);
    return 0;
}

可见LMK向内核线程注册了一个shrinker的监听回调,实现体为lowmem_shrinker。当系统的空闲页面低于一定阈值时,这个回调就会被执行。

Lowmemorykiller.c 中定义了两个数组,分别如下:

static short lowmem_adj[6] = {
    0,
    1,
    6,
   12,
 };
static int lowmem_adj_size = 4;//下面的数值以此为单位(页大小)
static int lowmem_minfree[6] = {
    3 * 512,    /* 6MB */
    2 * 1024,   /* 8MB */
    4 * 1024,   /* 16MB */
    16 * 1024,  /* 64MB */
};

第一个数组lowmem_adj最多有6个元素,默认只定义了4个,它表示可用容量处于”某层级”时需要被处理的adj值;第二个数组则是对”层级”的描述。这样说可能不清楚,举个例子,lowmem_minfree 的第一个元素是3*512,3*512*lowmem_adj_size=6MB.意味着当可用内存小于6MB时,Killer需要清理adj的值为0(即lowmem_adj的第一个元素)以下的那些进程。其中adj的取值范围是-17~15,数字越小表示进程级别越高,通常只有0-15被使用。

下图是LWK机制的实现简图。

LWK实现简图

这两个数组只是系统的预定义值,我们可以根据项目的实际需求来定制。

Android系统提供了相应的文件来供我们修改这两组值。

路径如下:

/sys/module/lowmemorykiller/parameters/adj
/sys/module/lowmemorykiller/parameters/minfree

可以在 init.rc(系统启动时,由init进程解析的第一个脚本)中加入如下语句。init.rc路径为/system/core/rootdir/init.rc

/sys/module/lowmemorykiller/parameters/adj  0,8
/sys/module/lowmemorykiller/parameters/minfree 1024,4096

Android进程分类

进程omm_adj的大小跟进程的类型以及进程被调度的次序有关。

在Android中,进程主要分为以下几个种类:

1. 前台进程(foreground)

  目前正在屏幕上显示的进程和一些系统进程。举例来说,Dialer,Storage,Google Search等系统进程就是前台进程;再举例来说,当你运行一个程序,如浏览器,当浏览器界面在前台显示时,浏览器属于前台进程(foreground),但一旦你按home回到主界面,浏览器就变成了后台程序(background)。我们最不希望终止的进程就是前台进程。

2. 可见进程(visible)

  可见进程是一些不再前台,但用户依然可见的进程,举个例来说:widget、输入法等,都属于visible。这部分进程虽然不在前台,但与我们的使用也密切相关,我们也不希望它们被终止(你肯定不希望时钟、天气,新闻等widget被终止,那它们将无法同步,你也不希望输入法被终止,否则你每次输入时都需要重新启动输入法)。

3. 桌面进程(home app)

  即launcher,保证在多任务切换之后,可以快速返回到home界面而不需重新加载launcher。

4. 次要服务(secondary server)

  目前正在运行的一些服务(主要服务,如拨号等,是不可能被进程管理终止的,故这里只谈次要服务),举例来说:谷歌企业套件,Gmail内部存储,联系人内部存储等。这部分服务虽然属于次要服务,但很一些系统功能依然息息相关,我们时常需要用到它们,所以也不太希望他们被终止。

5. 后台进程(hidden)

  即是后台进程(background),就是我们通常意义上理解的启动后被切换到后台的进程,如浏览器,阅读器等。当程序显示在屏幕上时,他所运行的进程即为前台进程(foreground),一旦我们按home返回主界面(注意是按home,不是按back),程序就驻留在后台,成为后台进程(background)。后台进程的管理策略有多种:有较为积极的方式,一旦程序到达后台立即终止,这种方式会提高程序的运行速度,但无法加速程序的再次启动;也有较消极的方式,尽可能多的保留后台程序,虽然可能会影响到单个程序的运行速度,但在再次启动已启动的程序时,速度会有所提升。这里就需要用户根据自己的使用习惯找到一个平衡点。

6. 内容供应节点(content provider)

没有程序实体,进提供内容供别的程序去用的,比如日历供应节点,邮件供应节点等。在终止进程时,这类程序应该有较高的优先权。

7. 空进程(empty)

  没有任何东西在内运行的进程,有些程序,比如BTE,在程序退出后,依然会在进程中驻留一个空进程,这个进程里没有任何数据在运行,作用往往是提高该程序下次的启动速度或者记录程序的一些历史信息。这部分进程无疑是应该最先终止的

在AMS 用于处理进程的相关代码文件ProcessList.java 中,定义了不同类型的adj值,如下所示:

/**
*省略其它代码
*/
    //未知的adj
    static final int UNKNOWN_ADJ = 16;
    static final int CACHED_APP_MAX_ADJ = 15;
    static final int CACHED_APP_MIN_ADJ = 9;
    //B list of service ,和A list相比,对用户黏合度小一些
    static final int SERVICE_B_ADJ = 8;
    //用户前一次交互的进程
    static final int PREVIOUS_APP_ADJ = 7;
    //Launcher进程
    static final int HOME_APP_ADJ = 6;
    //运行了application service的进程
    static final int SERVICE_ADJ = 5;
    //重量级应用程序进程
    static final int HEAVY_WEIGHT_APP_ADJ = 4;
    //用于承载backup相关操作的进程
    static final int BACKUP_APP_ADJ = 3;
    //这类进程能被用户感觉到但不可见,如后台运行的音乐播放器
    static final int PERCEPTIBLE_APP_ADJ = 2;
    //有前台可见的Activity进程,如果轻易杀死这类进程,将严重影响用户体验
    static final int VISIBLE_APP_ADJ = 1;
    //当前正在运行的那个进程,即用户正在交互的程序
    static final int FOREGROUND_APP_ADJ = 0;
    static final int PERSISTENT_SERVICE_ADJ = -11;
    //persistent性质的进程,如telephony
    static final int PERSISTENT_PROC_ADJ = -12;
    //系统进程
    static final int SYSTEM_ADJ = -16;

/**
*省略其它代码
*/

上面定义的adj数值来看,adj越小表示进程类型就越重要,特别的,系统进程的默认oom_adj 为-16,这类进程永远不会被杀死。

还需要注意的是updateOomLevels函数,内部原理是通过写上面两个文件来实现,AMS 会根据系统的当前配置自动修正adj和minfree,以尽可能适配不同的硬件。函数源码如下所示:

private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
        // Scale buckets from avail memory: at 300MB we use the lowest values to
        // 700MB or more for the top values.
        float scaleMem = ((float)(mTotalMemMb-300))/(700-300);

        // Scale buckets from screen size.
        int minSize = 480*800;  //  384000
        int maxSize = 1280*800; // 1024000  230400 870400  .264
        float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
        if (false) {
            Slog.i("XXXXXX", "scaleMem=" + scaleMem);
            Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth
                    + " dh=" + displayHeight);
        }

        float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp;
        if (scale < 0) scale = 0;
        else if (scale > 1) scale = 1;
        int minfree_adj = Resources.getSystem().getInteger(
                com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust);
        int minfree_abs = Resources.getSystem().getInteger(
                com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);
        if (false) {
            Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);
        }

        // We've now baked in the increase to the basic oom values above, since
        // they seem to be useful more generally for devices that are tight on
        // memory than just for 64 bit.  This should probably have some more
        // tuning done, so not deleting it quite yet...
        final boolean is64bit = false; //Build.SUPPORTED_64_BIT_ABIS.length > 0;

        for (int i=0; i<mOomAdj.length; i++) {
            int low = mOomMinFreeLow[i];
            int high = mOomMinFreeHigh[i];
            mOomMinFree[i] = (int)(low + ((high-low)*scale));
            if (is64bit) {
                // On 64 bit devices, we consume more baseline RAM, because 64 bit is cool!
                // To avoid being all pagey and stuff, scale up the memory levels to
                // give us some breathing room.
                mOomMinFree[i] = (3*mOomMinFree[i])/2;
            }
        }

        if (minfree_abs >= 0) {
            for (int i=0; i<mOomAdj.length; i++) {
                mOomMinFree[i] = (int)((float)minfree_abs * mOomMinFree[i]
                        / mOomMinFree[mOomAdj.length - 1]);
            }
        }

        if (minfree_adj != 0) {
            for (int i=0; i<mOomAdj.length; i++) {
                mOomMinFree[i] += (int)((float)minfree_adj * mOomMinFree[i]
                        / mOomMinFree[mOomAdj.length - 1]);
                if (mOomMinFree[i] < 0) {
                    mOomMinFree[i] = 0;
                }
            }
        }

        // The maximum size we will restore a process from cached to background, when under
        // memory duress, is 1/3 the size we have reserved for kernel caches and other overhead
        // before killing background processes.
        mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024) / 3;

        // Ask the kernel to try to keep enough memory free to allocate 3 full
        // screen 32bpp buffers without entering direct reclaim.
        int reserve = displayWidth * displayHeight * 4 * 3 / 1024;
        int reserve_adj = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);
        int reserve_abs = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute);

        if (reserve_abs >= 0) {
            reserve = reserve_abs;
        }

        if (reserve_adj != 0) {
            reserve += reserve_adj;
            if (reserve < 0) {
                reserve = 0;
            }
        }

        if (write) {
            ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
            buf.putInt(LMK_TARGET);
            for (int i=0; i<mOomAdj.length; i++) {
                buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
                buf.putInt(mOomAdj[i]);
            }

            writeLmkd(buf);
            SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
        }
        // GB: 2048,3072,4096,6144,7168,8192
        // HC: 8192,10240,12288,14336,16384,20480
    }

改变进程权重adj值

除了系统的评定标准,我们也可以用自己的方式来改变进程的权重值。

1.写文件

  和上述提到的adj和minfree类似,进程的oom_adj也可以通过写文件的形式来修改,路径为/proc/< PID >/oom_adj。比如init.rc中有如下语句

on early-init
 # Set init and its forked children's oom_adj.
 write /proc/1/oom_adj -16

PID 为1的进程为init程序,将此进程的oom_adj 设置为-16,保证它不会被杀死。

2.设置android:persistent

  对某些非常重要的程序,不希望它被系统杀死。在AndroidMainifest.xml文件中给application 标签添加”android:persistent=true”属性,将应用程序设置为常驻内存,但需要特别注意,如果应用程序本不够完善,而系统又不能正常回收,那么会导致意想不到的问题。


参考资料

《深入理解Android内核设计思想》

作者:ylyg050518 发表于2016/9/21 1:38:03 原文链接
阅读:155 评论:0 查看评论

Activity详解三 启动activity并返回结果

$
0
0

 

1 简介

、如果想在Activity中得到新打开Activity 关闭后返回的数据,需要使用系统提供的startActivityForResult(Intent intent, int requestCode)方法打开新的Activity,新的Activity 关闭后会向前面的Activity传回数据,为了得到传回的数据,必须在前面的Activity中重写onActivityResult(int requestCode, int resultCode, Intent data)方法。

public class MainActivity extends Activity {

    private final static String TAG="MainActivity";

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

       

        Button btnOpen=(Button)this.findViewById(R.id.btnOpen);

        btnOpen.setOnClickListener(new View.OnClickListener(){

            public void onClick(View v) {

                //得到新打开Activity关闭后返回的数据

                //第二个参数为请求码,可以根据业务需求自己编号

                startActivityForResult(new Intent(MainActivity.this, OtherActivity.class), 1);

            }

        });

    }

    /**

     * 为了得到传回的数据,必须在前面的Activity中(指MainActivity类)重写onActivityResult方法

     *

     * requestCode 请求码,即调用startActivityForResult()传递过去的值

     * resultCode 结果码,结果码用于标识返回数据来自哪个新Activity

     */

    @Override

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        String result = data.getExtras().getString("result");//得到新Activity 关闭后返回的数据

        Log.i(TAG, result);

    }

}

当新Activity关闭后,新Activity返回的数据通过Intent进行传递,android平台会调用前面Activity 的onActivityResult()方法,把存放了返回数据的Intent作为第三个输入参数传入,在onActivityResult()方法中使用第三个输入参数可以取出新Activity返回的数据。

                    

2 setResult

使用startActivityForResult(Intent intent, int requestCode)方法打开新的Activity,新Activity关闭前需要向前面的Activity返回数据需要使用系统提供的setResult(int resultCode, Intent data)方法实现:

public class OtherActivity extends Activity {
 
    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.other);
        Button btnClose=(Button)findViewById(R.id.btnClose);

        btnClose.setOnClickListener(new View.OnClickListener(){

            public void onClick(View v) {

                //数据是使用Intent返回

                Intent intent = new Intent();

                //把返回数据存入Intent

                intent.putExtra("result", "My name is linjiqin");

                //设置返回数据

                OtherActivity.this.setResult(RESULT_OK, intent);

                //关闭Activity

                OtherActivity.this.finish();

            }

        });

       

    }

 

}

setResult()方法的第一个参数值可以根据业务需要自己定义,上面代码中使用到的RESULT_OK是系统Activity类定义的一个常量,值为-1,代码片断如下:

public class android.app.Activity extends ......{

  public static final int RESULT_CANCELED = 0;

  public static final int RESULT_OK = -1;

  public static final int RESULT_FIRST_USER = 1;

}

 

说明:当点击“打开新的Activity”按钮,会跳转到“我是新打开的Activity”页面;

        当点击“关闭”按钮,关闭当前页面,同时跳转到“我是旧的Activity”页面,且会传递result参数给前一个Activity

3请求码的作用                

使用startActivityForResult(Intent intent, int requestCode)方法打开新的Activity,我们需要为startActivityForResult()方法传入一个请求码(第二个参数)。请求码的值是根据业务需要由自已设定,用于标识请求来源。例如:一个Activity有两个按钮,点击这两个按钮都会打开同一个Activity,不管是那个按钮打开新Activity,当这个新Activity关闭后,系统都会调用前面Activity的onActivityResult(int requestCode, int resultCode, Intent data)方法。在onActivityResult()方法如果需要知道新Activity是由那个按钮打开的,并且要做出相应的业务处理,这时可以这样做:

 

@Override  public void onCreate(Bundle savedInstanceState) {

        ....

        button1.setOnClickListener(new View.OnClickListener(){

            public void onClick(View v) {

                startActivityForResult (new Intent(MainActivity.this, NewActivity.class), 1);

           }

        });

        button2.setOnClickListener(new View.OnClickListener(){

            public void onClick(View v) {

                 startActivityForResult (new Intent(MainActivity.this, NewActivity.class), 2);

            }

        });

                          

       @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {

               switch(requestCode){

                   case 1:

                   //来自按钮1的请求,作相应业务处理

                   case 2:

                   //来自按钮2的请求,作相应业务处理

                }

          }

}

  

4 结果码的作用

在一个Activity中,可能会使用startActivityForResult()方法打开多个不同的Activity处理不同的业务,当这些新Activity关闭后,系统都会调用前面Activity的onActivityResult(int requestCode, int resultCode, Intent data)方法。为了知道返回的数据来自于哪个新Activity,在onActivityResult()方法中可以这样做(ResultActivity和NewActivity为要打开的新Activity):

public class ResultActivity extends Activity {

       .....

       ResultActivity.this.setResult(1, intent);

       ResultActivity.this.finish();

}

public class NewActivity extends Activity {

       ......

        NewActivity.this.setResult(2, intent);

        NewActivity.this.finish();

}

public class MainActivity extends Activity { // 在该Activity会打开ResultActivity和NewActivity

       @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {

               switch(resultCode){

                   case 1:

                   // ResultActivity的返回数据

                   case 2:

                    // NewActivity的返回数据

                }

          }

} 

  

5 Demo源码:

TestResultActivity.java

package mm.shandong.com.testresult;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;

public class TestResultActivity extends AppCompatActivity {
    EditText editTextBrand;
    RadioGroup radioGroup;
    TextView textViewXH;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_result);
        editTextBrand = (EditText) findViewById(R.id.editTextBrand);
        radioGroup = (RadioGroup) findViewById(R.id.radioGroup);
        textViewXH = (TextView) findViewById(R.id.textViewXH);
        RadioButton radionButton = (RadioButton) radioGroup.getChildAt(0);
        radionButton.setChecked(true);
    }
    ///跳转到选择品牌界面
    public void selectBrand(View view) {
        Intent intent = new Intent(this, TestResultActivity1.class);
        startActivityForResult(intent, 1);
    }
   ///选择要购买的电脑
    public void selectCompute(View view) {
        Intent intent = new Intent(this, TestResultActivity2.class);
        String brand = editTextBrand.getText().toString();
        RadioButton radionButton =
                (RadioButton) radioGroup.findViewById(radioGroup.getCheckedRadioButtonId());
        String nc = radionButton.getText().toString();
        intent.putExtra("brand", brand);
        intent.putExtra("nc", nc);
        startActivityForResult(intent, 2);
        textViewXH.setText("");
    }
    ///activity请求返回的回调
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        switch (requestCode) { //resultCode为回传的标记
            case 1:
                if (resultCode == 2) {
                    String brand = intent.getStringExtra("brand");
                    editTextBrand.setText(brand);
                }
                break;
            case 2:
                if (resultCode == 3) {
                    String xh = intent.getStringExtra("xh");
                    textViewXH.setText(xh);
                }

                break;
        }
    }
}


TestResultActivity1

package mm.shandong.com.testresult;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class TestResultActivity1 extends AppCompatActivity {
    ListView listView;
    String[] brands = new String[]{"联想", "戴尔"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_result1);
        listView = (ListView) findViewById(R.id.listView);
        ArrayAdapter arrayAdapter = new ArrayAdapter(this,
                    android.R.layout.simple_list_item_checked, brands);
        listView.setAdapter(arrayAdapter);
        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

    }
   ///提交选择的品牌结果
    public void submit(View view) {
        int index = listView.getCheckedItemPosition();
        if (index < 0) {
            Toast.makeText(this, "请选择品牌", Toast.LENGTH_SHORT).show();
            return;
        }
        String brand = (String) listView.getItemAtPosition(index);
        int resultCode = 2;
        Intent intent = getIntent();
        intent.putExtra("brand", brand);
        setResult(2, intent);
        finish();
    }
}

TestResultActivity2

package mm.shandong.com.testresult;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TestResultActivity2 extends AppCompatActivity {
    ListView listView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_result2);
        Intent intent=getIntent();
        String brand=intent.getStringExtra("brand");
        String nc=intent.getStringExtra("nc");
        Map map=initData();
        List<String> lists= (List<String>) map.get(brand+nc);
        listView= (ListView) findViewById(R.id.listView);
        ArrayAdapter arrayAdapter=new ArrayAdapter(this,
                             android.R.layout.simple_list_item_checked,lists);
        listView.setAdapter(arrayAdapter);
        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    }
    ///提交选择的电脑型号
    public void submit(View view){
        int index=  listView.getCheckedItemPosition();
        if(index<0){
            Toast.makeText(this,"请选择型号",Toast.LENGTH_SHORT).show();
            return ;
        }
        String xh= (String) listView.getItemAtPosition(index);
        int resultCode=2;
        Intent intent=getIntent();
        intent.putExtra("xh",xh);
        setResult(3,intent);
        finish();
    }
    ///初始化数据源
    public  Map initData(){
        Map map=new HashMap();
        List<String> lists=new ArrayList<>();
        lists.add("联想1G0001");
        lists.add("联想1G0002");
        map.put("联想1G",lists);
        lists=new ArrayList<>();
        lists.add("联想2G0001");
        lists.add("联想2G0002");
        map.put("联想2G",lists);
        lists=new ArrayList<>();
        lists.add("戴尔1G0001");
        lists.add("戴尔1G0002");
        map.put("戴尔1G",lists);
        lists=new ArrayList<>();
        lists.add("戴尔2G0001");
        lists.add("戴尔2G0002");
        map.put("戴尔2G",lists);
        return map;
    }
}


本人微博:honey_11

Demo下载
最后,以上例子都来源与安卓无忧,请去应用宝或者豌豆荚下载:例子源码,源码例子文档一网打尽

作者:androidWuYou 发表于2016/9/21 8:17:07 原文链接
阅读:254 评论:0 查看评论

面试题集锦

$
0
0

基本知识

1.静态方法可以重写吗


父类的普通方法可以被继承和重写,不多作解释,如果子类继承父类,而且子类没有重写父类的方法,但是子类会有从父类继承过来的方法。

静态的方法可以被继承,但是不能重写。如果父类中有一个静态的方法,子类也有一个与其方法名,参数类型,参数个数都一样的方法,并且也有static关键字修饰,那么该子类的方法会把原来继承过来的父类的方法隐藏,而不是重写。通俗的讲就是父类的方法和子类的方法是两个没有关系的方法,具体调用哪一个方法是看是哪个对象的引用;这种父子类方法也不在存在多态的性质。《Java编程思想》中这样提到“只有普通的方法调用可以是多态的”。
>
何为静态?静态方法是类在加载时就被加载到内存中的方法,在整个运行过程中保持不变,因而不能重写。但非静态方法是在对象实例化时才单独申请内存空间,为每一个实例分配独立的运行内存,因而可以重写。

2.ArrayList和Vector的主要区别是什么?

1、Vector是多线程安全的,而ArrayList不是,这个可以从源码中看出,Vector类中的方法很多有synchronized进行修饰,这样就导致了Vector在效率上无法与ArrayList相比;

2、两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是不同的,很多网友说Vector增加原来空间的一倍,ArrayList增加原来空间的50%,其实也差不多是这个意思,不过还有一点点问题可以从源码中看出,一会儿从源码中分析。

3、Vector可以设置增长因子,而ArrayList不可以,最开始看这个的时候,我没理解什么是增量因子,不过通过对比一下两个源码理解了这个,先看看两个类的构造方法:

3.RemoteView在哪些功能中使用

桌面小部件则是通过AppWidgetProvider来实现的,AppWidget本质是一个广播.

通知栏和桌面小部件的开发过程中都会用到RemoteView,它们在更新界面时无法像在Activity里面那样直接更新View,这是因为两者的界面都运行在其他线程中,确切的说是系统的SystemServer进程.为了跨进程更新界面,RemoteViews提供一系列set方法,并且这些方法只是View全部方法的子集,另外RemoteVIew支持的View类型也是有限的。

4.SurfaceView和View的区别是什么?

surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面。那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中 thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。

5.讲一下android中进程的优先级?

在Android中进程按优先级可以分为五类,优先级从高到低排列:
- 前台进程 该进程包含正在与用户进行交互的界面组件,比如一个Activity
- 可视进程 该进程中的组件虽然没有和用户交互,但是仍然可以被看到
- 服务进程 该进程包含在执行后台操作的服务组件,比如播放音乐的进程
- 后台进程 该进程包含的组件没有与用户交互,用户也看不到
- 空进程 没有任何界面组件、服务组件,或触发器组件**
Android系统是进程托管的,也就是说进程都是由系统来管理,系统会按照特定的算来来回收这些进程。在回收中秉承几个原则
1. 尽量延长进程的生命周期,不到必须的情况下不会回收,因为系统回收进程会影响用户体验
2. 按优先级从低到高进行回收
3. 同等优先级的进程越近使用越晚回收。
进程过一段时间后是会被回收的,但要遵循上面的这些原则,播放音乐的这个进程的优先级还是比较高的,所以被莫名其妙地回收的可能性不大,在播放音乐时平白无故地停止这样的情况很少对吧?service和application的生命周期有关,只要进程被回收,那么它所占用的所有资源将被回收。

6.静态变量持有Activity引用会导致内存泄露

仔细分析在一个静态成员的变量中保留了Activity里面的一个视图,可恶的是视图中保留了Context的引用,这个时候就产生了一个对象长期被引用,导致Activity无法被GC掉,Activity占用的内存也无法被GC掉。这个时候内存溢出发生了。
浅谈Android开发中内存泄露与优化
http://m.blog.csdn.net/article/details?id=50581404
http://www.linuxidc.com/Linux/2015-12/126432.htm

7.jni的开发流程

eclipse的jni的开发
dnk环境搭建
先要用编译,在bin里,用javah编译,生成.h文件。
编写Android.mk文件
用gunstep生成so文件。
用Android studio 来开发jni

8多线程、同步异步、线程池

并发:
多个用户争夺同一个资源(这个资源可以是服务器上的日志,可以是执行某一此sql操作,可以使ftp服务器上的某个文件等,又或者是程序中的某一个全局变量,因此我们可以称这种资源为:全局资源);
解释:
并发是在多个用户请求同一个资源的时候,或者是程序本身多线程请求同一个资源的时候造成的。
比如:
一个财务系统,两个人同时对总钱数进行操作,一个加10块一个减100块,注意这两个操作是同时进行的,那系统就不知道是加还是减了,这是并发问题。或者,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线程的处理的数据,而B线程又修改了A线程处理的数理(线程安全)。

异步:
A线程要请求某个资源,但是此资源正在被B线程使用中,因为没有同步机制存在,A线程
仍然请求的到这个资源,A线程无需等待。
同步:
A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求
不到,怎么办,A线程只能等待下去。

同步与异步:
显然,同步最安全,最保险的。而异步不安全,容易导致死锁,这样一个线程死掉就会导致整个
进程崩溃,但没有同步机制的存在,性能会有所提升。所以对于同步与异步必须有所取舍。

8.1线程池

8.2同步机制的实现

Java同步机制有4种实现方式:(部分引用网上资源)

① ThreadLocal ② synchronized( ) ③ wait() 与 notify() ④ volatile

目的:都是为了解决多线程中的对同一变量的访问冲突
ThreadLocal
ThreadLocal 保证不同线程拥有不同实例,相同线程一定拥有相同的实例,即为每一个使用该
变量的线程提供一个该变量值的副本,每一个线程都可以独立改变自己的副本,而不是与其它线程的副本冲突。
优势:提供了线程安全的共享对象
与其它同步机制的区别:同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信;而 ThreadLocal 是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源,这样当然不需要多个线程进行同步了。

volatile
volatile 修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。

而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。
优势:这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
缘由:Java 语言规范中指出,为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而

且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样当多个线程同时与某

个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。而 volatile 关键字就

是提示 VM :对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
使用技巧:在两个或者更多的线程访问的成员变量上使用 volatile 。当要访问的变量已在

synchronized 代码块中,或者为常量时,不必使用。
线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的

是B。只在某些动作时才进行A和B的同步,因此存在A和B不一致的情况。volatile就是用来避免这种

情况的。 volatile告诉jvm,它所修饰的变量不保留拷贝,直接访问主内存中的(读操作多时使用

较好;线程间需要通信,本条做不到)

Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自

动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的

一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。

您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理

想的线程安全,必须同时满足下面两个条件:

对变量的写操作不依赖于当前值;该变量没有包含在具有其他变量的不变式中。

sleep() vs wait()
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,把执行机会给其他线程,但是监

控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁

定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁

进入运行状态。

(如果变量被声明为volatile,在每次访问时都会和主存一致;如果变量在同步方法或者同步块中

被访问,当在方法或者块的入口处获得锁以及方法或者块退出时释放锁时变量被同步。)

8.3 java同步机制:synchronized

9.view相关

9.1自定义view
9.2图表,柱状图
9.3新特性view
Android开发之RecyclerView的使用全解

10.http反馈码

HTTP协议状态码表示的意思主要分为五类 ,大体是 :
1××   保留
2××   表示请求成功地接收
3××   为完成请求客户需进一步细化请求
4××   客户错误
5××   服务器错误

11.asyctask的实现原理

AsyncTask的本质是一个线程池,所有提交的异步任务都会在这个线程池中的工作线程内执行,当工作线程需要跟UI线程交互时,工作线程会通过向在UI线程创建的Handler(原理见:《Handler+Looper+MessageQueue深入详解》)传递消息的方式,调用相关的回调函数,从而实现UI界面的更新。
1、 AsyncTask的本质是一个静态的线程池,AsyncTask派生出的子类可以实现不同的异步任务,这些任务都是提交到静态的线程池中执行。

2、线程池中的工作线程执行doInBackground(mParams)方法执行异步任务

3、当任务状态改变之后,工作线程会向UI线程发送消息,AsyncTask内部的InternalHandler响应这些消息,并调用相关的回调函数

12.handler的是怎样实现的

handler不是不可以在子线程里生成,生成时,需要prepare,否则报错。
具体见博客。

13.ArrayList和LinkedList的区别

一般大家都知道ArrayList和LinkedList的大致区别:
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

14.序列化Serializable和Parcelable的理解和区别

15.65k 64k个方法的限制

当Android系统启动一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt。DexOpt的执行过程是在第一次加载Dex文件的时候执行的。这个过程会生成一个ODEX文件,即Optimised Dex。执行ODex的效率会比直接执行Dex文件的效率要高很多。但是在早期的Android系统中,DexOpt有一个问题,也就是这篇文章想要说明并解决的问题。DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面。但是这个链表的长度是用一个short类型来保存的,导致了方法id的数目不能够超过65536个。当一个项目足够大的时候,显然这个方法数的上限是不够的。尽管在新版本的Android系统中,DexOpt修复了这个问题,但是我们仍然需要对老系统做兼容。
android studio 中这样做

android{
    defaultConfig {
        ...
         // Enabling multidex support.
         multiDexEnabled true
    }

    dexOptions {
        javaMaxHeapSize "4g" //set the max heap size for dexing to 4GB.

        incremental true
    }
}

dependencies {
    //compile fileTree(include: ['*.jar'], dir: 'libs')
    //compile project(':library')
    compile 'com.android.support:multidex:1.0.1'
    ...
}

16.webview和js的交互,cordova,crosswalkwebview,react native

2.框架知识

利用了什么框架
框架是何种原理

2.1afinal

github项目地址

2.2http android Android http请求代码

HttpClient和HttpURLConnection的区别

okhttp 图片上传 断点续传

github地址
android 介绍Retrofit的简单使用
Retrofit原理及调用流程分析

xutils

github地址
Android-xUtils框架原理分析

im

比较好的,就是下面这俩个,
腾讯im 免费,企业和个人
网易云信 个人有期限,企业收费

bug上报实时

bugly
bghd

二维码 中兴

图片加载框架

视频播放

vitamio
ijkplayer
vlc

rxjava

ButterKnife基本使用

https://github.com/JakeWharton/butterknife

3.模式知识

3.1观察者模式

3.2单例模式

4.技术博客

为知笔记

github项目

Android:学习AIDL,这一篇文章就够了(上)

MVC以外的另两种软件架构(ORM,IOC)

android–动态加载、插件化

mvp
使用新版Android Studio检测内存泄露和性能

5.做过的项目,开发环境

移动项目:开发,项目经理
个人仓库
无线电管理监测
智能玩具交互
党建加油站 视频播放器
混合开发:rn,cordova

后台:
spring hibernate
node

作者:u014624241 发表于2016/9/21 8:39:51 原文链接
阅读:395 评论:0 查看评论

Android官方开发文档Training系列课程中文版:电池续航时间优化之监测电池电量及充电状态

$
0
0

原文地址:http://android.xsoftlab.net/training/monitoring-device-state/index.html

引言

作为一款优秀的APP应用,应该总是想方设法的降低电量的消耗。通过这节课的学习,你将有能力使APP可以基于设备的状态来调整APP的功能及行为。

我们可以通过比如在断开连接时关闭后台服务,或者在电量低的时候降低更新的频率等等手段来降低电量的消耗。

监测电池电量及充电状态

在更改后台的更新频次时,检查当前的电池电量及充电状态是我们先要做的。

应用程序的更新频率取决于电池的电量以及充电状态。由于设备处于充电状态时应用的耗电量几乎可以忽略,所以,在设备连接到充电器时,你可以将应用的刷新频率开到最大,如果设备没有在充电,那么降低更新频率可以延长电池的使命时间。

检查当前的充电状态

首先我们需要检查当前的充电状态。BatteryManager会将电池信息以及充电信息通过粘性Intent将其广播。

因为是粘性Intent,所以不需要注册BroadcastReceiver,只需要在调用registerReceiver()时传一个null就可以,当前的电池状态由该方法直接返回。你也可以在这里传递一个BroadcastReceiver对象,但是我们接下来的处理方式并不是在其中做的,所以这并不是必须的。

IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);

如果设备当前处于充电状态,那么可以获得当前的充电状态,无论它是通过USB还是通过AC适配器充电的。

// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                     status == BatteryManager.BATTERY_STATUS_FULL;
// How are we charging?
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;

通常的做法是:应当是在连接到AC电源适配器时,将后台的更新频率加到最大,如果当前处于USB状态,这个频率应当适当降低,如果断开充电,则应当进一步降低。

监测充电状态的变化

设备的充电状态很容易随着充电器的插入、拔出而发生变化。所以随着充电状态的变化应当相应的调整应用的刷新频率。

当设备插上充电器或是拔出充电器时,BatteryManager都会广播一个Action,所以应当注册一个BroadcastReceiver用来监听这些事件。在清单文件中需要定义ACTION_POWER_CONNECTEDACTION_POWER_DISCONNECTED的意图过滤器。

<receiver android:name=".PowerConnectionReceiver">
  <intent-filter>
    <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
    <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
  </intent-filter>
</receiver>

在该BroadcastReceiver内,你可以获取当前的充电状态:

public class PowerConnectionReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) { 
        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
        boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                            status == BatteryManager.BATTERY_STATUS_FULL;

        int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
        boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
        boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;
    }
}

检查电池的剩余电量

在一些情况下还需要检查设备的剩余电量。当电量较低时可能需要降低应用的后台服务频率。

你可以通过以下方式获得设备的剩余电量:

int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level / (float)scale;

监测电量的重要变化

应用不能一直连续不断的监听电池的状态。

通常来说,一直不断的监听电池电量会使监听电池的任务大于应用的实际任务,所以最好是只监听一些比较重要的变更事件。

下面的清单文件摘自一段广播接收器内。该广播接收器会在电池的电量很低时或者是在电量恢复到安全水平时被触发。它监听了两个事件:ACTION_BATTERY_LOWACTION_BATTERY_OKAY.

<receiver android:name=".BatteryLevelReceiver">
<intent-filter>
  <action android:name="android.intent.action.ACTION_BATTERY_LOW"/>
  <action android:name="android.intent.action.ACTION_BATTERY_OKAY"/>
  </intent-filter>
</receiver>

通常情况下,在电量很低时要关闭所有的后台更新。加载在使用APP之前,手机关机了,那么应用的数据是否是最新的就没那么重要了。

在很多情况下,手机充电时是被放在一个固定的位置上的。下节课我们将会学习如何检查设备的放置环境以及如何监测设备的放置状态。

作者:u011064099 发表于2016/9/21 8:40:01 原文链接
阅读:307 评论:0 查看评论

android-pdf-viewer在android studio应用问题说明

$
0
0

android-pdf-viewer在android studio应用问题说明

小白一枚,之前一直是做.NET开发的,最近需要弄一个新闻app,能力有限,只能借助HTML5 WebAPP+android studio来完成这项工作。
android studio主要用WebView来加载发布好的WebApp,打包生产APP。
其中由于显示一些pdf文档,所以研究了一下,记录一下心得,同时也希望帮助到新手们。

android 显示网络pdf,基本原理:先将pdf文件通过DownloadManager下载到手机sdk某个文件夹中,然后通过android-pdf-viewer插件进行显示。

android-pdf-viewer插件可以直接到github上下载,地址:https://github.com/barteksc/AndroidPdfViewer
或者直接到发布好的页面下载:https://github.com/barteksc/AndroidPdfViewer/releases
我下载的是:AndroidPdfViewer-2.1.0版本zip包

将下载AndroidPdfViewer-2.1.0包解压出来,再你的app项目中,打开 File -> New -> Import Module 选择到刚才解压的文件夹

本人目录是:D:\AndroidStudioProjects\AndroidPdfViewer-2.1.0\android-pdf-viewer

这里写图片描述
完成后,回提示先ERROR:
Error:Plugin with id ‘com.github.dcendents.android-maven’ not found.

解决方法:
点击你的工程Gradle Scripts目录下的bulid.gradle (Project:你的工程名)

buildscript {
repositories {
jcenter()
}
dependencies {
classpath ‘com.android.tools.build:gradle:2.0.0’
}
}
中dependencies的下添加 classpath ‘com.github.dcendents:android-maven-gradle-plugin:1.3’,
Mark Modules之后,出现新的ERROR:
Error:Plugin with id ‘com.jfrog.bintray’ not found.
此时同样在dependencies下添加:classpath “com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0”
再次Mark Modules就不会报错了。
先就可以开始使用 android-pdf-viewer 插件了,

别忘记引用权限问题哦

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

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"/>
    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />

先代码为项目中report activity.java,实现了接收另外一个activity跳转过来同时传递参数pdf地址,然后通过DownloadManager下载完成之后,显示pdf。

package cn.cgrs.myphone;

import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.provider.OpenableColumns;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.TextView;
import android.widget.Toast;

import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener;
import com.github.barteksc.pdfviewer.listener.OnPageChangeListener;
import com.github.barteksc.pdfviewer.scroll.DefaultScrollHandle;
import com.shockwave.pdfium.PdfDocument;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;

public class Report extends AppCompatActivity  implements OnPageChangeListener, OnLoadCompleteListener {

    private com.github.barteksc.pdfviewer.PDFView pdfView ;
    private TextView textView;

    private DownloadManager downloadManager;
    private SharedPreferences prefs;
    private static String DL_ID = "downloadId";

    Integer pageNumber = 0;

    String pdfFileName = "yyy.pdf";

    Uri uri;

    String AUrl;

    private Boolean isDown = false;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_report);

        Intent intent = getIntent();   // 获取 Intent

        AUrl  = intent.getStringExtra("url"); // 获取 String 值
        Log.e("接收url:",AUrl);

        //AUrl = "http://www.tyyq.cn/xhsapp/download/a03790b7f27243eeada01537a2ce2f77.pdf";

        String[] exts = AUrl.split("/");
        pdfFileName = exts[exts.length-1];
        Log.e("pdf文件名:",pdfFileName);

        String pdfName = Environment.getExternalStorageDirectory() +
                "/download";

        File file = new File(pdfName, pdfFileName);

        pdfView = (com.github.barteksc.pdfviewer.PDFView)findViewById(R.id.pdfView);
        textView = (TextView) findViewById(R.id.textView);

        if(file.exists()){
            Log.e("Tip:","报告已经存在!");
            //文件已经存在,则直接显示
            uri = Uri.fromFile(file);
            displayFromUri(uri);
            textView.setVisibility(View.GONE);
            pdfView.setVisibility(View.VISIBLE);
        }
        else{

            isDown = true;

        }

        //Log.e("prefs字符串:",prefs.toString());
    }

    @Override
    protected void onResume(){

        super.onResume();

        if(isDown)
        {
            try{
                Log.e("Tip:","报告不存在,需要下载!");
                DL_ID = pdfFileName;
                //文件不存在需要先下载
                downloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);
                prefs = PreferenceManager.getDefaultSharedPreferences(this);

                StartReport();
            }
            catch (Exception ex)
            {
                Toast.makeText(this, ex.getMessage(), Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        //unregisterReceiver(receiver);

        try {
            unregisterReceiver(receiver);
        } catch (IllegalArgumentException e) {
            if (e.getMessage().contains("Receiver not registered")) {
                // Ignore this exception. This is exactly what is desired
            } else {
                // unexpected, re-throw
                throw e;
            }
        }
    }

    //下载报告启动函数
    protected void StartReport() {
        // TODO Auto-generated method stub

        if(!prefs.contains(DL_ID) || true) {  //
           // String url = AUrl;
            //Log.e("-----",AUrl);

            //String[] exts = url.split("/");
            //pdfFileName = "97b49c0822c14a01b3ebc273679bc6bf.pdf";
                    //exts[exts.length-1];

            String url = AUrl;//"http://www.tyyq.cn/RollImage/11.pdf";
            Log.e("开始下载url:",AUrl);

            //开始下载
            Uri resource = Uri.parse(encodeGB(url));
            DownloadManager.Request request = new DownloadManager.Request(resource);
            request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
            request.setAllowedOverRoaming(false);
            //设置文件类型
            MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
            String mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(url));
            request.setMimeType(mimeString);
            //在通知栏中显示
            //设置通知栏标题
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
            request.setDescription("舆情报告正在下载");
            //request.setShowRunningNotification(true);
            request.setVisibleInDownloadsUi(true);
            //sdcard的目录下的download文件夹
            request.setDestinationInExternalPublicDir("/download/", pdfFileName);
            request.setTitle("舆情报告");
            long id = downloadManager.enqueue(request);
            //保存id
            prefs.edit().putLong(DL_ID, id).commit();
        } else {

            Log.e("提示:","下载已经开始,检查状态");
            //下载已经开始,检查状态
            queryDownloadStatus();
        }

        registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    }

    /**
     * 如果服务器不支持中文路径的情况下需要转换url的编码。
     * @param string
     * @return
     */
    public String encodeGB(String string)
    {
        //转换中文编码
        String split[] = string.split("/");
        for (int i = 1; i < split.length; i++) {
            try {
                split[i] = URLEncoder.encode(split[i], "GB2312");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            split[0] = split[0]+"/"+split[i];
        }
        split[0] = split[0].replaceAll("\\+", "%20");//处理空格
        return split[0];
    }

    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //这里可以取得下载的id,这样就可以知道哪个文件下载完成了。适用与多个下载任务的监听
            Log.v("intent", ""+intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0));
            queryDownloadStatus();
        }
    };

    private void queryDownloadStatus() {
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(prefs.getLong(DL_ID, 0));
        Cursor c = downloadManager.query(query);
        if(c.moveToFirst()) {
            int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
            switch(status) {
                case DownloadManager.STATUS_PAUSED:
                    Log.v("down", "STATUS_PAUSED");
                case DownloadManager.STATUS_PENDING:
                    Log.v("down", "STATUS_PENDING");
                case DownloadManager.STATUS_RUNNING:
                    //正在下载,不做任何事情
                    Log.v("down", "STATUS_RUNNING");
                    break;
                case DownloadManager.STATUS_SUCCESSFUL:
                    //完成
                    Log.v("down", "下载完成");

                    String pdfName = Environment.getExternalStorageDirectory() +
                            "/download";

                    File file = new File(pdfName, pdfFileName);
                    uri = Uri.fromFile(file);

                    Log.e("----",uri.toString());

                    displayFromUri(uri);

                    textView.setVisibility(View.GONE);
                    pdfView.setVisibility(View.VISIBLE);
                    break;
                case DownloadManager.STATUS_FAILED:
                    //清除已下载的内容,重新下载
                    Log.v("down", "STATUS_FAILED");
                    downloadManager.remove(prefs.getLong(DL_ID, 0));
                    prefs.edit().clear().commit();
                    break;
            }
        }
    }


    /*pdf显示函数集合*/

    private void displayFromUri(Uri urii) {
        //pdfFileName = getFileName(urii);

        pdfView.fromUri(urii)
                .defaultPage(pageNumber)
                .onPageChange(this)
                .enableAnnotationRendering(true)
                .onLoad(this)
                .scrollHandle(new DefaultScrollHandle(this))
                .load();
    }


    public void onResult(int resultCode, Intent intent) {
        if (resultCode == RESULT_OK) {
            uri = intent.getData();
            displayFromUri(uri);
        }
    }

    @Override
    public void onPageChanged(int page, int pageCount) {
        pageNumber = page;
        setTitle(String.format("%s %s / %s", pdfFileName, page + 1, pageCount));
    }

    public String getFileName(Uri uri) {
        String result = null;
        if (uri.getScheme().equals("content")) {
            Cursor cursor = getContentResolver().query(uri, null, null, null, null);
            try {
                if (cursor != null && cursor.moveToFirst()) {
                    result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
                }
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
        }
        if (result == null) {
            result = uri.getLastPathSegment();
        }
        return result;
    }

    @Override
    public void loadComplete(int nbPages) {
        PdfDocument.Meta meta = pdfView.getDocumentMeta();

        printBookmarksTree(pdfView.getTableOfContents(), "-");

    }

    public void printBookmarksTree(List<PdfDocument.Bookmark> tree, String sep) {
        for (PdfDocument.Bookmark b : tree) {

            // Log.e(TAG, String.format("%s %s, p %d", sep, b.getTitle(), b.getPageIdx()));

            if (b.hasChildren()) {
                printBookmarksTree(b.getChildren(), sep + "-");
            }
        }
    }

}
作者:W3031213101 发表于2016/9/21 8:54:38 原文链接
阅读:333 评论:0 查看评论

函数main_loop和u-boot命令执行

$
0
0
一.main_loop函数执行流程和命令解释器
run_main_loop是board_r中函数运行列表init_fnc_t init_sequence_r[]最后一个函数,它又调用了main_loop,且run_main_loop永不返回。
static int run_main_loop(void)
{
    /* main_loop() can return to retry autoboot, if so just run it again */
    for (;;)
        main_loop();
    return 0;
}
main_loop定义在common/main.c中:
void main_loop(void)
{
    const char *s;
    bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
    modem_init();
#ifdef CONFIG_VERSION_VARIABLE
    setenv("ver", version_string);  /* set version variable */
#endif /* CONFIG_VERSION_VARIABLE */
    cli_init();
    run_preboot_environment_command();
    s = bootdelay_process();
    if (cli_process_fdt(&s))
        cli_secure_boot_cmd(s);
    autoboot_command(s);
    cli_loop();
}
bootstage_mark_name函数调用了show_boot_progress,利用它显示启动进程(progress),此处为空函数。
setenv设置环境变量ver为version_string,后者在common/cmd_version.c中定义为:
const char __weak version_string[] = U_BOOT_VERSION_STRING;

U_BOOT_VERSION_STRING在version.h中定义为:

#define U_BOOT_VERSION_STRING U_BOOT_VERSION " (" U_BOOT_DATE " - " \
    U_BOOT_TIME " " U_BOOT_TZ ")" CONFIG_IDENT_STRING
其中U_BOOT_VERSION ,U_BOOT_DATE,U_BOOT_TIME,U_BOOT_TZ均由u-boot构建系统自动产生,
它们分别代表u-boot版本号,编译日期和时间,以及时间区。
如果定义了CONFIG_SYS_HUSH_PARSER,那么配置u-boot使用hush shell来作为执行器。hush shell是一种轻量型的shell。
cli_init用来初始化hush shell使用的一些变量。hush shell的实现机制比较复杂,以下的hush shell相关实现代码都不做详尽跟踪分析。
有兴趣的可参见源代码和相关的网络文章。

run_preboot_environment_command函数从环境变量中获取"preboot"的定义,该变量包含了一些预启动命令,
一般环境变量中不包含该项配置。
bootdelay_process从环境变量中取出"bootdelay"和"bootcmd"的配置值,将取出的"bootdelay"配置值转换成整数,
赋值给全局变量stored_bootdelay,最后返回"bootcmd"的配置值。bootdelay为u-boot的启动延时计数值,计数期间内
如无用户按键输入干预,那么将执行"bootcmd"配置中的命令。
由于没有定义CONFIG_OF_CONTROL,函数cli_process_fdt返回false,接下来执行autoboot_command,
该函数在common/autoboot.c中实现:

void autoboot_command(const char *s)
{
    if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
        run_command_list(s, -1, 0);
    }

}
全局变量stored_bootdelay在上面已做说明。静态函数abortboot中包含了CONFIG_AUTOBOOT_KEYED宏预处理分支,该宏定义用来使能用户名密码登录,这里它没有定义,而后调用了abortboot_normal,在执行的时间stored_bootdelay(秒)内,如无用户按键输入干预,那么abortboot_normal函数将返回0,否则返回1。 当无用户按键干预时,接下来将调用run_command_list执行上述从环境变量中读取的"bootcmd"配置值。注意该函数的参数s。run_command_list中调用了hush shell的命令解释器(parse_stream_outer函数),解释bootcmd中的启动命令。环境变量bootcmd中的启动命令,用来设置linux必要的启动环境,然后加载和启动linux内核。u-boot启动linux内核后,将控制权交给linux内核,至此不再返回。
如用户在设定的bootdelay内无按键输入,那么将运行cli_loop执行hush shell命令解释器:
void cli_loop(void)
{

    parse_file_outer();
    /* This point is never reached */
    for (;;);

}
parse_file_outer进行必要的初始化后,也将调用hush shell的命令解释器,即parse_stream_outer函数:
static int parse_stream_outer(structin_str*inp,intflag)
{
    do {
        ...
        ...
        run_list(...);
    } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP) &&  //#define FLAG_EXIT_FROM_LOOP 1
            (inp->peek != static_peek || b_peek(inp)));
}
上面的do-while会循环命令解析器的"命令输入解析--执行"运行模式。
其中的函数run_list执行如下的函数调用流程:
run_list-->run_list_real-->run_pipe_real
最后在函数run_pipe_real中有:
return cmd_process(...);
函数cmd_process最后完成u-boot命令的定位和执行。
二.u-boot命令执行
命令处理函数均在common/command.c中实现,上述函数cmd_process定义如下:
enum command_ret_t cmd_process(int flag, int argc, char * const argv[],  int *repeatable, ulong *ticks)
{
    enum command_ret_t rc = CMD_RET_SUCCESS;
    cmd_tbl_t *cmdtp;
    /* Look up command in command table */
    cmdtp = find_cmd(argv[0]);
    if (cmdtp == NULL) {
        printf("Unknown command '%s' - try 'help'\n", argv[0]);
        return 1;
    }
    /* found - check max args */
    if (argc > cmdtp->maxargs)
        rc = CMD_RET_USAGE;
    /* If OK so far, then do the command */
    if (!rc) {
        if (ticks)
            *ticks = get_timer(0);
        rc = cmd_call(cmdtp, flag, argc, argv);
        if (ticks)
            *ticks = get_timer(*ticks);
        *repeatable &= cmdtp->repeatable;
    }
    if (rc == CMD_RET_USAGE)
        rc = cmd_usage(cmdtp);
    return rc;
}
u-boot中使用宏U_BOOT_CMD来定义命令,该宏在include/command.h中定义如下:
#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)      \
    U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)
U_BOOT_CMD是宏U_BOOT_CMD_COMPLETE最后一个参数_comp为NULL的特例,_comp表示变量是否自动完成:
#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
    ll_entry_declare(cmd_tbl_t, _name, cmd) =           \  /*注意这里是cmd而非_cmd*/
        U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,  \
                        _usage, _help, _comp);
其中包含的宏U_BOOT_CMD_MKENT_COMPLETE定义为:
#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,      \
                _usage, _help, _comp)           \
        { #_name, _maxargs, _rep, _cmd, _usage,         \
            _CMD_HELP(_help) _CMD_COMPLETE(_comp) }

上面的_CMD_HELP根据配置可选为使用完整或简短帮助说明。_CMD_COMPLETE则根据配置决定是否使用自动完成函数。U_BOOT_CMD_MKENT_COMPLETE宏其实是组织输入参数,对ll_entry_declare进行数据填充。ll_entry_declare在文件include/linker_lists.h中定义:

#define ll_entry_declare(_type, _name, _list)               \
    _type _u_boot_list_2_##_list##_2_##_name __aligned(4)       \
            __attribute__((unused,              \
            section(".u_boot_list_2_"#_list"_2_"#_name)))
参数_type为cmd_tbl_t,这里定义一个cmd_tbl_t结构体,并把它放在符号段.u_boot_list_2_"#_list"_2_"#_name中,其中的_list和_name根据宏参数进行字符串替换。
下面,我们举例说明上述宏的实现机制。比如有如下的定义:
U_BOOT_CMD(
    env, CONFIG_SYS_MAXARGS, 1, do_env,
    "environment handling commands", env_help_text
);    

U_BOOT_CMD_COMPLETE (
    env, CONFIG_SYS_MAXARGS, 1, do_env,
    "environment handling commands", env_help_text,NULL
);
带入宏参及其展开为 :
U_BOOT_CMD_COMPLETE(env, CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text , NULL ) \
    ll_entry_declare(cmd_tbl_t, env , cmd) =           \
        U_BOOT_CMD_MKENT_COMPLETE(env , CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text , NULL);
其中的ll_entry_declare带入宏参及其展开为 :
ll_entry_declare(cmd_tbl_t , env , cmd )            \
    cmd_tbl_t _u_boot_list_2_cmd_2_env __aligned(4)       \
            __attribute__((unused,              \
            section(".u_boot_list_2_cmd_2_env )))
其中的U_BOOT_CMD_MKENT_COMPLETE带入宏参及其展开为:
U_BOOT_CMD_MKENT_COMPLETE(env , CONFIG_SYS_MAXARGS , 1, do_env , _usage, _help, _comp)     \
        { "env", CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text ,NULL}
那么上述U_BOOT_CMD_COMPLETE最终展开为:
cmd_tbl_t _u_boot_list_2_cmd_2_env __aligned(4)       \
            __attribute__((unused, section(".u_boot_list_2_cmd_2_env ))) =
{ "env", CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text ,NULL}
其中的cmd_tbl_t定义为:
<pre code_snippet_id="1890747" snippet_file_name="blog_20160921_20_3676404" name="code" class="cpp">struct cmd_tbl_s {
    char        *name;      /* Command Name         */
    int     maxargs;    /* maximum number of arguments  */
    int     repeatable; /* autorepeat allowed?      */
                    /* Implementation function  */
    int     (*cmd)(struct cmd_tbl_s *, int, int, char * const []);
    char        *usage;     /* Usage message    (short) */
#ifdef  CONFIG_SYS_LONGHELP
    char        *help;      /* Help  message    (long)  */
#endif
#ifdef CONFIG_AUTO_COMPLETE
    /* do auto completion on the arguments */
    int     (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
typedef struct cmd_tbl_s    cmd_tbl_t;

该结构体包含了命令名,命令实现函数,命令使用简短说明usage的输出字符串,帮助回调函数,参数变量自动完成函数等。u-boot使用该结构体来描述一个完整的命令。

U_BOOT_CMD_COMPLETE宏用来定义一个cmd_tbl_t结构体变量,初始化该结构体中的相应成员,并把该结构体变量存放在4字节对齐的.u_boot_list_2_cmd_2_env符号段中。如前所述,宏U_BOOT_CMD将最后一个参数_comp置为NULL,对U_BOOT_CMD_COMPLETE做了进一步的封装。所有使用U_BOOT_CMD和U_BOOT_CMD_COMPLETE定义的命令最后都集中放在以.u_boot_list_2_cmd_2开头的符号段中。即.u_boot_list_2_cmd_2_##name,这里的name是命令名。

我们回到函数上述的命令处理函数cmd_process,被其调用的find_cmd将根据命令名查找相应的cmd_tbl_t变量符号段,其实现如下:

cmd_tbl_t *find_cmd(const char *cmd)
{
    cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
    const int len = ll_entry_count(cmd_tbl_t, cmd);
    return find_cmd_tbl(cmd, start, len);
}
ll_entry_start定义如下:
#define ll_entry_start(_type, _list)                    \
({                                  \
    static char start[0] __aligned(4) __attribute__((unused,    \
        section(".u_boot_list_2_"#_list"_1")));         \
    (_type *)&start;                        \
})
那么,在上述函数find_cmd中,语句  
cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
定义一个包含0个字节的数组start[0],且把它放在.u_boot_cmd_2_list_1段中,该段属性为unsued。注意在u-boot.lds中有:
 .u_boot_list : {
  KEEP(*(SORT(.u_boot_list*)));    
.u_boot_list中所有符号是按字符表的先后顺序排列的,.u_boot_list_2_list_1会放在所有使用U_BOOT_CMD和U_BOOT_CMD_COMPLETE
定义的符号段的最前面,即.u_boot_cmd_2_list_1为以.u_boot_list_2_cmd_2开头的符号段中的第一个。它定义为0个字节的数组start[0],
编译器并不为它分配存储空间,那么它将指向以.u_boot_list_2_cmd_2开头的符号段中的第一个符号。
同理ll_entry_end用end[0]来标识.u_boot_list_2_cmd_2_xxx段的结尾,接下来的函数ll_entry_count返回的就是start - end的值,
即符号段.u_boot_list_2_cmd_2_xxx总字节长度。然后调用find_cmd_tbl,根据传入的.u_boot_list_2_cmd_2_xxx段的首地址和
函数ll_entry_count 返回的长度,根据命令名查找相应的符号段,即相命令对应的cmd_tbl_t结构体变量,然后返回该结构体指针。
find_cmd_tbl的实现如下:
cmd_tbl_t *find_cmd_tbl(const char *cmd, cmd_tbl_t *table, int table_len)
{
    cmd_tbl_t *cmdtp;
    cmd_tbl_t *cmdtp_temp = table;  /* Init value */
    const char *p;
    int len;
    int n_found = 0;
    if (!cmd)
        return NULL;
     len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
    for (cmdtp = table; cmdtp != table + table_len; cmdtp++) {
        if (strncmp(cmd, cmdtp->name, len) == 0) {
            if (len == strlen(cmdtp->name))
                return cmdtp;  /* full match */
            cmdtp_temp = cmdtp; /* abbreviated command ? */
            n_found++;
        }
    }
    if (n_found == 1) {            /* exactly one match */
        return cmdtp_temp;
    }
    return NULL;   /* not found or ambiguous command */
}
查找到命令名对应的cmd_tbl_t结构体变量后,cmd_process接下来将调用函数cmd_call执行cmd_tbl_t中的命令。
cmd_process中相应的代码段如下:
  if (!rc) {
        if (ticks)
            *ticks = get_timer(0);
        rc = cmd_call(cmdtp, flag, argc, argv);
        if (ticks)
            *ticks = get_timer(*ticks);
        *repeatable &= cmdtp->repeatable;
    }
    if (rc == CMD_RET_USAGE)
        rc = cmd_usage(cmdtp);
变量ticks用来记录命令的执行时间,repeatable为命令是否自动重复执行标志。这两个变量都将返回到上层的调用函数。
函数cmd_call利用传入的参数,直接调用cmdtp->cmd,即:
 (cmdtp->cmd)(cmdtp, flag, argc, argv);
最后,如果命令执行的返回值为CMD_RET_USAGE,代表命令执行出错,且置标CMD_RET_USAGE ,那么将调用cmd_usage,
输出简短的命令使用帮助信息。cmd_usage实现如下:
int cmd_usage(const cmd_tbl_t *cmdtp)
{
    printf("%s - %s\n\n", cmdtp->name, cmdtp->usage);
#ifdef  CONFIG_SYS_LONGHELP
    printf("Usage:\n%s ", cmdtp->name);
    if (!cmdtp->help) {
        puts ("- No additional help available.\n");
        return 1;
    }
    puts(cmdtp->help);
    putc('\n');
#endif  /* CONFIG_SYS_LONGHELP */
    return 1;
}
三.u-boot中的子命令
部分u-boot的命令包含子命令,如env命令,它由子命令save,set,edit等组成。类似的还有sf命令。这些主命令执行时必须指定子命令。
u-boot中子命令的实现不再使用上面的gcc关键字section来指定段,只是直接定义了一个cmd_tbl_t表,并使用子命令及其运行参数初始化该表。对于上述讨论中使用U_BOOT_CMD定义的命令,它们是散放在文件各处的,很难用一个全局的cmd_tbl_t表将这些命令统一定义初始化。而子命令不同,它只定义在一个或少量文件中,该cmd_tbl_t表为static类型,可以在定义时直接填充。当然,主命令还是由宏U_BOOT_CMD来定义引入。

作者:metersun 发表于2016/9/21 9:11:14 原文链接
阅读:239 评论:0 查看评论

7 种常用的排序算法直观感受

$
0
0

1. 快速排序

介绍:

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。

步骤:

从数列中挑出一个元素,称为 “基准”(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

排序效果:
这里写图片描述

2. 归并排序

介绍:

归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用

步骤:

申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
设定两个指针,最初位置分别为两个已经排序序列的起始位置
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针达到序列尾
将另一序列剩下的所有元素直接复制到合并序列尾

排序效果:
这里写图片描述

3. 堆排序

介绍:

堆积排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

步骤:

(比较复杂,自己上网查吧)

排序效果:
这里写图片描述

4. 选择排序

介绍:

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。

排序效果:
这里写图片描述

5. 冒泡排序

介绍:

冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

步骤:

比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

排序效果:
这里写图片描述

6. 插入排序

介绍:

插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

步骤:

从第一个元素开始,该元素可以认为已经被排序
取出下一个元素,在已经排序的元素序列中从后向前扫描
如果该元素(已排序)大于新元素,将该元素移到下一位置
重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
将新元素插入到该位置中
重复步骤2

排序效果:

(暂无)

7. 希尔排序

介绍:

希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

1、插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率

2、但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位>

排序效果:
这里写图片描述

回头有时间把每一种算法的代码实现也给放上去就完美了,欢迎各位查找bug,提出自己的见解!

作者:zhi137_zhi148_qwer 发表于2016/9/21 9:24:27 原文链接
阅读:576 评论:0 查看评论

android---UI---RecyclerView实现瀑布流(2)

$
0
0

前言:

前面介绍了瀑布流的基本实现,实际上瀑布流还有一些事件需要监听。比如点击事件,下拉和上拉事件。
这里接着上次的 android—UI—RecyclerView实现瀑布流(1)


添加Item点击事件:

参考文章:为RecyclerView添加item的点击事件
RecyclerView侧重的是布局的灵活性,虽说可以替代ListView但是连基本的点击事件都没有,这篇文章就来详细讲解如何为RecyclerView的item添加点击事件,顺便复习一下观察者模式

最终目的

所以我们的目的就是要模拟ListView的setOnItemClickListener()方法,调用者只须调用类似于setOnItemClickListener的东西就能获得被点击item的相关数据。

实现原理:

为RecyclerView的每个子item设置setOnClickListener,然后在onClick中再调用一次对外封装的接口,将这个事件传递给外面的调用者。而“为RecyclerView的每个子item设置setOnClickListener”在Adapter中设置。其实直接在onClick中也能完全处理item的点击事件,但是这样会破坏代码的逻辑。

实现步骤:

在这里为了不影响前面的数据,重写一个适配器adapter:

MyAdapter.java

具体步骤如下:
1.继承接口 OnClickListener

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener{ }

2.在MyAdapter中定义如下接口,模拟ListView的OnItemClickListener:

//定义接口
public static interface OnRecyclerViewItemClickListener {
        void onItemClick(View view , String data);
    }

3.声明一个这个接口的变量:

private OnRecyclerViewItemClickListener mOnItemClickListener = null;

4.和前面的ViewHolder不同,这里使用了自定义的ViewHolder
ViewHoider的作用就是一个储存器,所以自定义的ViewHolder继承于
RecyclerView.ViewHolder—RecyclerView强制使用ViewHolder,而在ListView里面,ViewHolder只是作为一个优化的选项。

//自定义的ViewHolder,持有每个Item的的所有界面元素
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mTextView;
        public ImageView mImgView;
        public ViewHolder(View view){
            super(view);
            mTextView = (TextView) view.findViewById(R.id.masonry_item_title);
            mImgView=(ImageView) view.findViewById(R.id.masonry_item_img);
        }
    }

5.在onCreateViewHolder()中为每个item添加点击事件

@Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup,  int viewType) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
        ViewHolder vh = new ViewHolder(view);
        //将创建的View注册点击事件
        view.setOnClickListener(this);
        return vh;
    }

6.将点击事件转移给外面的调用者:(这也是继承接口OnClickListener必须实现的方法)

@Override
    public void onClick(View v) {
        if (mOnItemClickListener != null) {
            //注意这里使用getTag方法获取数据
            mOnItemClickListener.onItemClick(v,(String)v.getTag());
        }
    }

7.前面一步有个getTag方法获取数据,那就有个setTag方法对应。在onBindViewHolder()方法里面。

@Override
    public void onBindViewHolder(ViewHolder viewHolder,  int position) {
        viewHolder.mTextView.setText(products.get(position).getTitle());
        viewHolder.mImgView.setImageResource(products.get(position).getImg());
        //将数据保存在itemView的Tag中,以便点击时进行获取
        viewHolder.itemView.setTag(products.get(position).getTitle());
    }

8.最后暴露给外面的调用者,定义一个设置Listener的方法():

public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
        this.mOnItemClickListener = listener;
    }

总的MyAdapter代码:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener{
    private List<Product> products;
    public MyAdapter(List<Product> list) {
        products = list;
    }

    private OnRecyclerViewItemClickListener mOnItemClickListener = null;

    //define interface
    public static interface OnRecyclerViewItemClickListener {
        void onItemClick(View view , String data);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.masonry_item, viewGroup, false);
        ViewHolder vh = new ViewHolder(view);
        //将创建的View注册点击事件
        view.setOnClickListener(this);
        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder,  int position) {
        viewHolder.mTextView.setText(products.get(position).getTitle());
        viewHolder.mImgView.setImageResource(products.get(position).getImg());
        //将数据保存在itemView的Tag中,以便点击时进行获取
        viewHolder.itemView.setTag(products.get(position).getTitle());
    }

    @Override
    public void onClick(View v) {
        if (mOnItemClickListener != null) {
            //注意这里使用getTag方法获取数据
            mOnItemClickListener.onItemClick(v,(String)v.getTag());
        }
    }

    public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
        this.mOnItemClickListener = listener;
    }


    //获取数据的数量
    @Override
    public int getItemCount() {
        return products.size();
    }
    //自定义的ViewHolder,持有每个Item的的所有界面元素
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mTextView;
        public ImageView mImgView;
        public ViewHolder(View view){
            super(view);
            mTextView = (TextView) view.findViewById(R.id.masonry_item_title);
            mImgView=(ImageView) view.findViewById(R.id.masonry_item_img);
        }
    }
}

然后在Activity中调用即可

adapter.setOnItemClickListener(new MyAdapter.OnRecyclerViewItemClickListener(){
            @Override
            public void onItemClick(View view , String data){
                Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
            }
        });

测试:

这里写图片描述

当然还可以添加点击样式:drawable添加颜色选择器:
item_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/colorAccent" android:state_pressed="true"></item>
    <item android:drawable="@color/white"></item>
</selector>

设置item的背景色为item_bg即可
这里写图片描述

总结:

以上所有步骤都发生在自定义的adapter中,典型的观察者模式,有点绕的地方在于,这里涉及到两个观察者模式的使用,view的setOnClickListener本来就是观察者模式,我们将这个观察者模式的事件监听传递给了我们自己的观察者模式。

上拉和下拉事件的监听:

在下拉刷新,上拉加载的事件中,第一步就是要监听上啦和下拉事件。
这个很简单,由于自带了OnScrollListener方法

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                Log.d("ScrollListener",dx+","+dy+"");
            }
        });

位置变化:
这里写图片描述

可以看出上拉为负下拉为正。

上拉加载:

首先判断是否已经到底部:
判断方法1:使用StaggeredGridLayoutManager获取最后一个的位置,判断是否在屏幕底部。

StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
               if (lastPositions == null) {
                   lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
               }
               staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
               lastVisibleItemPosition = findMax(lastPositions);

判断方法2: RecyclerView每加载一个item都会调用一次onBindViewHolder方法,并且只在item由不可见变为可见的时候才会调用此方法。我们可以通过onBindViewHolder方法来判断是否已经到达列表的底部。–>RecyclerView滑动到底部自动加载

public void onBindViewHolder(CollectionViewHolder holder, int position) {
        holder.fillData(mData.get(position));
        if(position == getItemCount()-1){//已经到达列表的底部
            loadMoreData();
        }
    }

这里采用第一种方法:
具体如下:
设置两个变量:

      //最后一个的位置

    private int[] lastPositions;


     //最后一个可见的item的位置

    private int lastVisibleItemPosition;

滑动事件监听:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                int visibleItemCount = layoutManager.getChildCount();
                int totalItemCount = layoutManager.getItemCount();
                Log.i("onScrollStateChanged", "visibleItemCount" + visibleItemCount);
                Log.i("onScrollStateChanged", "lastVisibleItemPosition" + lastVisibleItemPosition);
                Log.i("onScrollStateChanged", "totalItemCount" + totalItemCount);
                Log.i("newstate","newstate"+newState);
                if (visibleItemCount > 0 && newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItemPosition == totalItemCount - 2) {
                    Log.d("----------","到底了");

                }

            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                //Log.d("ScrollListener",dx+","+dy+"");
                RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
                if (lastPositions == null) {
                    lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
                }
                staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
                lastVisibleItemPosition = findMax(lastPositions);

            }
        });

首先在onScrolled方法里监听滚动事件的最后一个Item的位置。
1.获取View的layoutManager。

    RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
    taggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;

2.getSpanCount()返回所包含的 Item 总个数—为什么是个数组?

经过测试,lastPositions的长度和设置的列有关,设置3列,数组长度也为三。但是测试时候,在findLastVisibleItemPositions 方法之前,值都为0.经过findLastVisibleItemPositions 方法之后就能找到每一列的最大位置。—这里是27,28,29。然后比较,输出最大的位置。

if (lastPositions == null) {
                    lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
                }

3.通过位置判断哪个才是真正的最后一个

staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
lastVisibleItemPosition = findMax(lastPositions);

自定义findMax方法

private int findMax(int[] lastPositions) {
        int max = lastPositions[0];
        for (int value : lastPositions) {
            if (value > max) {
                max = value;
            }
        }
        return max;
    }

继续onScrollStateChanged()方法
1.同样先

                RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();

2.通过布局管理器获取:总Item数和可见item数

int visibleItemCount = layoutManager.getChildCount();
                int totalItemCount = layoutManager.getItemCount();

3.判断是否到底:lastVisibleItemPosition == totalItemCount - 1 因为是位置是从0开始的,所以比总数小1.

if (visibleItemCount > 0 && newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItemPosition == totalItemCount - 1) {
                    Log.d("----------","到底了");

                }

测试–

这里写图片描述

数据加载:
在适配器MyAdapter中添加两个方法:

//增加item
    public void addData(int position,String text,int Bitmap) {
        products.add(new Product(Bitmap, text));
        notifyItemInserted(position);
    }
   //删除item
    public void removeData(int position) {
        products.remove(position);
        notifyItemInserted(position);
    }

在拉到底部的时候调用:

if (visibleItemCount > 0 && newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItemPosition == totalItemCount - 1) {
                    Log.d("----------","到底了");
                    adapter.addData(totalItemCount,"添加",R.drawable.ic_9);

                }

测试:
这里写图片描述
这里写图片描述

下拉刷新:

下拉刷新要判断是否在顶部:使用SwipeRefreshLayout控件不需要判断是否在顶部,因为SwipeRefreshLayout控件默认在顶部才会刷新数据。
所以下面的方法可以作废。

同样的道理:findFirstVisibleItemPositions方法可以返回当前视图每一列可见的第一个item的位置。判断返回最小的是否等于0,等于就是到顶部了。

private int[] firstPositions;
private int firstVisibleItemPosition;
if (firstPositions == null) {
                    firstPositions = new int[staggeredGridLayoutManager.getSpanCount()];
                }
staggeredGridLayoutManager.findFirstVisibleItemPositions(firstPositions);
firstVisibleItemPosition = findMin(firstPositions);
private int findMin(int[] firstPositions) {
        int min = firstPositions[0];
        for (int value : firstPositions) {
            Log.d("xxxx",value+"");
            if (value < min) {
                min = value;
            }
        }
if(firstVisibleItemPosition==0){
                    Log.d("----------","到顶了");

                }

实现刷新功能:
SwipeRefreshLayout 是谷歌公司推出的用于下拉刷新的控件.
更改主布局文件为:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/swipe_refresh_widget"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
<android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>

关于SwipeRefreshLayout的使用:
SwipeRefreshLayout的使用

作者:lw_zhaoritian 发表于2016/9/21 10:26:43 原文链接
阅读:66 评论:0 查看评论

android5.0多种侧滑栏效果

$
0
0

1.普通侧滑
效果图:这里写图片描述
思路:通过自定义View继承HorizontalScrollView,然后重写onMeasure(),onLayout(),onTouchEvent()
方法并设置menu(通过动画使menu开始时处于隐藏状态)布局和content布局。(注意:使用ViewHelper类需要导入nineoldandroids-2.4.0.jar包)
menu(left_menu)布局代码:

<?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="wrap_content"
        android:orientation="vertical"
        android:layout_centerInParent="true">
        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">
            <ImageView
                android:id="@+id/id_img1"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_marginLeft="20dp"
                android:layout_marginTop="20dp"
                android:layout_centerVertical="true"
                android:src="@mipmap/img_1"/>
            <TextView
                android:id="@+id/iv_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="第一个item"
                android:textSize="21sp"
                android:textColor="#ffffff"
                android:layout_toRightOf="@+id/id_img1"
                android:layout_marginLeft="20dp"
                android:layout_centerVertical="true"/>
        </RelativeLayout>
        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">
            <ImageView
                android:id="@+id/id_img2"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_marginLeft="20dp"
                android:layout_marginTop="20dp"
                android:layout_centerVertical="true"
                android:src="@mipmap/img_2"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="第二个item"
                android:textSize="21sp"
                android:textColor="#ffffff"
                android:layout_toRightOf="@+id/id_img2"
                android:layout_marginLeft="20dp"
                android:layout_centerVertical="true"/>
        </RelativeLayout>
        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">
            <ImageView
                android:id="@+id/id_img3"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_marginLeft="20dp"
                android:layout_marginTop="20dp"
                android:layout_centerVertical="true"
                android:src="@mipmap/img_3"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="第三个item"
                android:textSize="21sp"
                android:textColor="#ffffff"
                android:layout_toRightOf="@+id/id_img3"
                android:layout_marginLeft="20dp"
                android:layout_centerVertical="true"/>
        </RelativeLayout>
        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">
            <ImageView
                android:id="@+id/id_img4"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_marginLeft="20dp"
                android:layout_marginTop="20dp"
                android:layout_centerVertical="true"
                android:src="@mipmap/img_4"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="第四个item"
                android:textSize="21sp"
                android:textColor="#ffffff"
                android:layout_toRightOf="@+id/id_img4"
                android:layout_marginLeft="20dp"
                android:layout_centerVertical="true"/>
        </RelativeLayout>
        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">
            <ImageView
                android:id="@+id/id_img5"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_marginLeft="20dp"
                android:layout_marginTop="20dp"
                android:layout_centerVertical="true"
                android:src="@mipmap/img_5"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="第五个item"
                android:textSize="21sp"
                android:textColor="#ffffff"
                android:layout_toRightOf="@+id/id_img5"
                android:layout_marginLeft="20dp"
                android:layout_centerVertical="true"/>
        </RelativeLayout>
    </LinearLayout>
</RelativeLayout>

content(activity_main)布局代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:hyname="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@mipmap/img_frame_background">
    <com.imooc.view.SlidingMenu
        android:id="@+id/id_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        hyname:rightPadding="100dp">
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal">
            <include layout="@layout/left_menu"/>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"
                android:background="@mipmap/qq">
                <Button
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="切换菜单"
                    android:onClick="toogleMenu"
                    android:textSize="21sp"/>
            </LinearLayout>
        </LinearLayout>
    </com.imooc.view.SlidingMenu>
</LinearLayout>

自定义attr.xml文件代码:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="rightPadding" format="dimension"/>
    <declare-styleable name="SlidingMenu">
        <attr name="rightPadding"></attr>
    </declare-styleable>
</resources>

自定义SlidingMenu代码:

public class SlidingMenu extends HorizontalScrollView {
    private LinearLayout mWapper;
    private ViewGroup mMenu;//菜单布局
    private ViewGroup mContent;//内容布局
    private int mScreenWidth;//屏幕宽度
    private int mMenuRightPadding=50;
    private boolean once;
    private int mMenuWidth;
    private boolean isOpen;

    public SlidingMenu(Context context) {
       this(context, null);
    }

    /**
     * 未使用自定义属性时,调用
     * @param context
     * @param attrs
     */
    public SlidingMenu(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    /**
     * 自定义了属性且使用时,调用次构造方法
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //获取定义的属性的数组
        TypedArray typedValue=context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyleAttr, 0);
        int n=typedValue.getIndexCount();
        for (int i=0;i<n;i++){
            int attr=typedValue.getIndex(i);
            switch (attr){
                case  R.styleable.SlidingMenu_rightPadding:
                mMenuRightPadding=typedValue.getDimensionPixelSize(attr,(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,50,context.getResources().getDisplayMetrics()));
                    break;
            }
        }
        typedValue.recycle();
        WindowManager mg= (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        //初始化屏幕信息对象
        DisplayMetrics outMetrics=new DisplayMetrics();
        //把屏幕的信息存储到DisplayMetrics中
        mg.getDefaultDisplay().getMetrics(outMetrics);
        //获取屏幕宽度赋值给mScreenWidth
        mScreenWidth=outMetrics.widthPixels;
    }

    /**
     * 设置子view的宽和高
     * 设置自己的宽和高
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if(!once){
            //获取SlidingMenu中的Linearlayout布局
            mWapper= (LinearLayout) getChildAt(0);
            //获取LinearLayout中的menu布局
            mMenu= (ViewGroup) mWapper.getChildAt(0);
            //获取LinearLayout中的Content布局
            mContent= (ViewGroup) mWapper.getChildAt(1);
            //获取menu宽度
            mMenuWidth= mMenu.getLayoutParams().width=mScreenWidth-mMenuRightPadding;
            //设置content的宽度
            mContent.getLayoutParams().width=mScreenWidth;
            mWapper.getLayoutParams().width=mScreenWidth;
            once=true;
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }


    /**
     * 通过设置偏移量,将menu隐藏
     * @param changed
     * @param l
     * @param t
     * @param r
     * @param b
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if(changed){
            this.scrollTo(mMenuWidth,0);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_UP:
                //隐藏在左边的宽度
                int scrollX=getScrollX();
                if (scrollX>=mMenuWidth/2){
                    this.smoothScrollTo(mMenuWidth,0);
                    isOpen=false;
                }else {
                    this.smoothScrollTo(0,0);
                    isOpen=true;
                }
                return true;
        }
        return super.onTouchEvent(ev);
    }

    public void openMenu(){
        if(isOpen)return;
        this.smoothScrollTo(0,0);
        isOpen=true;
    }

    public void closeMenu(){
        if(!isOpen)return;
        this.smoothScrollTo(mMenuWidth,0);
        isOpen=false;
    }

    //切换菜单
    public void toggle(){
        if(isOpen){
            closeMenu();
        }else {
            openMenu();
        }
    }
}

主文件代码:

public class MainActivity extends AppCompatActivity {
    private SlidingMenu mleftMenu;
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mleftMenu= (SlidingMenu) findViewById(R.id.id_menu);
        textView= (TextView) findViewById(R.id.iv_text);
        //menu的第一个Item的点击事件,可不写
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mleftMenu.toggle();
            }
        });
    }

    public void toogleMenu(View view){
        mleftMenu.toggle();
    }
}

2.抽屉式侧滑(一)
效果图:这里写图片描述
思路:在原来的基础上,在自定义View文件中重写onScrollChanged()方法
添加代码:

/**
     * 滚动时发生
     * @param l
     * @param t
     * @param oldl
     * @param oldt
     */
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        //调用属性动画,设置TranslateX,l值为menu隐藏的宽度,menu由完全隐藏变为完全可见,变化梯度                scale由1~0,menu偏移量由大到小;
        float scale=l*1.0f/mMenuWidth; //1~0
        ViewHelper.setTranslationX(mMenu, mMenuWidth * scale);
    }

3.抽屉式侧滑(二)
效果图:这里写图片描述
思路:在一的基础上通过设置menu的缩放效果,content的缩放效果和缩放中心实现。
实现代码:

/**
     * 滚动发生
     * @param l
     * @param t
     * @param oldl
     * @param oldt
     */
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        //调用属性动画,设置TranslateX,l值为menu隐藏的宽度,menu由完全隐藏变为完全可见,变化梯度scale由1~0,menu偏移量由大到小;
        float scale=l*1.0f/mMenuWidth; //1~0
//        ViewHelper.setTranslationX(mMenu, mMenuWidth * scale);
        float leftScale=1.0f-scale*0.3f;  //0.7~1.0
        float leftAlpha=0.6f+0.4f*(1-scale); //0.6~1.0
        float rightScale=0.7f+0.3f*scale; //1.0~0.7
        //缩放动画0.7~1.0
        ViewHelper.setScaleX(mMenu, leftScale);
        ViewHelper.setScaleY(mMenu, leftScale);
        //透明度变化0.6~1.0
        ViewHelper.setAlpha(mMenu, leftAlpha);
        ViewHelper.setTranslationX(mMenu, mMenuWidth * scale * 0.7f);
        ViewHelper.setPivotX(mContent, 0);
        ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
        //缩放动画1.0~0.7
        ViewHelper.setScaleX(mContent, rightScale);
        ViewHelper.setScaleY(mContent,rightScale);
    }

有不懂的地方可以到慕课网听讲解:http://www.imooc.com/learn/211
图片来源于:http://blog.csdn.net/lmj623565791/article/details/39257409博客。

作者:qq_27773675 发表于2016/9/21 10:28:00 原文链接
阅读:468 评论:1 查看评论

iOS项目拆分:数据本地持久化(4)

$
0
0

Core Data是有苹果官方提供的框架(#import <CoreData/CoreData.h>),实现数据持久化存储。Core Data实际上是将数据库的创建、表的创建、对象和表的转换等操作封装起来,极大的简化了操作。使用Core Data进⾏数据库存取不需要手动创建数据库,创建数据库的过程完全由Core Data框架自动完成,使用者需要做的就是把模型创建起来。

Core Date与SQLite相比较,SQLite比较原始,操作比较复杂,使用的是C的函数对数据库进行操作,但是SQLite可控性更强,并且能够跨平台。
  

以下是Core Data操作中经常使用的几个类:

1、NSManagedObjectModel:被管理的对象模型,相当于实体,不过它包含 了实体间的关系。
2、NSManagedObjectContext:被管理的对象上下文,相当于操作实际内容 作用:插入数据 查询 更新 删除
3、NSPersistentStoreCoordinator:持久化存储助理,用于数据库的连接器。
4、NSFetchRequest :获取数据的请求,用于数据查询,相当于查询语句。
5、NSPredicate:相当于查询条件。
6、NSEntityDescription:实体结构。
下面,讲解Core Data的简单使用

在创建项目的时候可以选择使用Core Data,项目创建成功后,在AppDelegate类中系统会自动添加相关代码,此外,还会自动生成一个数据模型文件 工程名.xcdatamodeld

这里写图片描述

AppDelegate.h类中对应生成的代码

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>//系统自动引入类
@interface AppDelegate : UIResponder <UIApplicationDelegate> 
@property (strong, nonatomic) UIWindow *window;
@property (readonly, strong) NSPersistentContainer *persistentContainer;
- (void)saveContext;
@end

AppDelegate.m类中对应生成的代码,此处注意在XCode中自动生成的代码较之前有所变动

#pragma mark - Core Data stack

@synthesize persistentContainer = _persistentContainer;

- (NSPersistentContainer *)persistentContainer {
    // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"Test123"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    // Replace this implementation with code to handle the error appropriately.
                    // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                    /*
                     Typical reasons for an error here include:
                     * The parent directory does not exist, cannot be created, or disallows writing.
                     * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                     * The device is out of space.
                     * The store could not be migrated to the current model version.
                     Check the error message to determine what the actual problem was.
                    */
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                    abort();
                }
            }];
        }
    }

    return _persistentContainer;
}

#pragma mark - Core Data Saving support

- (void)saveContext {
    NSManagedObjectContext *context = self.persistentContainer.viewContext;
    NSError *error = nil;
    if ([context hasChanges] && ![context save:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, error.userInfo);
        abort();
    }
}

如果在创建项目的时候没有勾选Use Core Data选项,但是在后面需要使用,需要手动添加一个Data Model文件和手动的添加AppDelegate中的相关代码

这里写图片描述

注:创建Data Model文件时需要注意,文件名称要与AppDelegate.m中managedObjectModel方法中提到的文件名称相匹配,ios8的Xcode排版发生了一些变动,需要注意

在生成Data Model文件后,在~~.xcdatamodeld里面添加实体和关系添加实体如图所示:

这里写图片描述
这里写图片描述
注:这里实际上就是向数据库中添加表格和建立表格之间的关联

完成以上步骤,数据库中表格的创建就已经完成,和使用SQLite比较,省略了sql语句以及调用C函数操作数据库的步骤,另外,在创建实体的时候不需要设置主键,实体对象的属性的类型是OC的类型,实体中其他实体对象类型是通过建立关系添加的。

创建好实体后,可以通过添加NSManagedObject subclass文件(注:创建模型对象的类, “Editor > Create NSManagedobject Subclass”。),系统可以自动添加实体对应的数据模型类,如图所示:
这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

以下是封装好的CoreDataManaer单例文件

#import <Foundation/Foundation.h>
@interface CoreDataManager : NSObject
/**
 *  单例的初始化类方法
 *  @return CoreDataManager
 */
+ (CoreDataManager *)defaultManager;
/**
 *  添加一个对象模型到数据库中
 *  @param name       模型类的名字
 *  @param dictionary 需要对应赋值的属性
 */
- (void)addManagedObjectModelWithName:(NSString *)name dictionary:(NSDictionary *)dictionary;
/**
 *  查询对象模型
 *  @param name      模型类的名字
 *  @param predicate 创建一个谓词
 *  @param sortkeys  用来排序的Keys(注意是个数组)
 *  @return 返回查到的对象, 在外部使用时应与name对应
 */
- (NSArray *)fetchManagedObjectModelWithName:(NSString *)name predicate:(NSPredicate *)predicate sortKeys:(NSArray *)sortkeys;
/**
 *  删除对象模型
 *  @param models 对象模型数组(注意是数组, 尽管是删除一个也要数组)
 */
- (void)deleteAllManagedObjectModels:(NSArray *)models;
@end
#import "CoreDataManager.h"
#import <CoreData/CoreData.h>
@interface CoreDataManager ()
@property (nonatomic, strong) NSManagedObjectContext * managedObjectContext;
@property (nonatomic, strong) NSManagedObjectModel * managedObjectModel;
@property (nonatomic, strong) NSPersistentStoreCoordinator * persistentStoreCoordinator;
@end
@implementation CoreDataManager
static CoreDataManager * s_defaultManager = nil;
+ (CoreDataManager *)defaultManager {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        s_defaultManager = [[CoreDataManager alloc] init];
    });
    return s_defaultManager;
}
/**
 *  单例的初始化方法, 在init方法中初始化单例类持有的对象
 *  @return 初始化后的对象
 */
- (instancetype)init
{
    self = [super init];
    if (self) {
        // 添加观察者, 当ManagerObjectContext发生变化时调用saveContext方法
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveContext) name:NSManagedObjectContextObjectsDidChangeNotification object:nil];
    }
    return self;
}
- (void)addManagedObjectModelWithName:(NSString *)name dictionary:(NSDictionary *)dictionary  {
    NSManagedObject * managerObject = [NSEntityDescription insertNewObjectForEntityForName:name inManagedObjectContext:self.managedObjectContext];
    [managerObject setValuesForKeysWithDictionary:dictionary];
}
- (NSArray *)fetchManagedObjectModelWithName:(NSString *)name predicate:(NSPredicate *)predicate sortKeys:(NSArray *)sortkeys {
    // 实例化查询请求
    NSFetchRequest * fetchRequest = [NSFetchRequest fetchRequestWithEntityName:name];
    // 谓词搜索如果没有谓词, 那么默认查询全部
    if (predicate) {
        [fetchRequest setPredicate:predicate];
    }
    // 如果没有用来排序的key, 那么默认不排序
    if (sortkeys) {
        // 如果有排序的Key就先创建一个数组来接收多个NSSortDescriptor对象(尽管是一个, 因为setSortDescriptors:方法需要数组作为参数)
        NSMutableArray * sortDescriptorKeys = [NSMutableArray new];
        // 遍历所有的用来排序的key
        for (NSString * key in sortkeys) {
            // 每有一个Key, 就使用该key来创建一个NSSortDescriptor
            NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:key ascending:YES];
            // 在sortDescriptorKeys数组中添加一个NSSortDescriptor元素
            [sortDescriptorKeys addObject:sortDescriptor];
        }
        // 查询请求设置排序方式
        [fetchRequest setSortDescriptors:sortDescriptorKeys];
    }
    // 使用数组来接收查询到的内容
    NSArray * fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
    // 如果数组为nil
    if (fetchedObjects == nil) {
        // 创建一个新的数组返回, 在外部去做判断
        fetchedObjects = [NSArray new];
    }
    // 返回查找到的数组
    return fetchedObjects;
}
- (void)deleteAllManagedObjectModels:(NSArray *)models {
    // 遍历删除传进来数组中的元素对应的表内容
    for (NSManagedObject * object in models) {
        // 使用管理者删除对象, 数组中的元素并没有缺少
        [self.managedObjectContext deleteObject:object];
    }
}
#pragma mark - Core Data stack
- (NSURL *)applicationDocumentsDirectory {
    NSLog(@"%@", [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]);
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
/**
 *  模型器的懒加载方法
 *  @return 唯一的模型器
 */
- (NSManagedObjectModel *)managedObjectModel {

    if (!_managedObjectModel) {

        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"PalmEfamily" withExtension:@"momd"];
        _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    }
    return _managedObjectModel;
}
/**
 *  链接器的懒加载方法
 *  @return 唯一的链接器对象
 */
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (!_persistentStoreCoordinator) {

        _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
        NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"PalmEfamily.sqlite"];
//        [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:nil];

        [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:@{NSInferMappingModelAutomaticallyOption : @YES, NSMigratePersistentStoresAutomaticallyOption : @YES } error:nil];
    }
    return _persistentStoreCoordinator;
}
/**
 *  管理者的懒加载方法
 *  @return 唯一的管理者对象
 */
- (NSManagedObjectContext *)managedObjectContext {
    if (!_managedObjectContext) {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
    }
    return _managedObjectContext;
}
/**
 *  ManagerObjectContext的保存方法
 */
- (void)saveContext {
    [self.managedObjectContext save:nil];
}
@end

以上是针对使用Core Data的一些总结,如存在问题请提出评论。

作者:Cituses 发表于2016/9/21 10:38:39 原文链接
阅读:264 评论:0 查看评论

ReplayKit库,iOS原生直播神器

$
0
0

版权声明:本文为博主原创,如需转载请注明出处。

前言

ReplayKit 是WWDC15推出的苹果原生录屏 API。在iOS9的时候主要提供的是录屏,录制完成后可以进行查看、编辑、通过指定方式分享出去。

在WWDC16上新版的 ReplayKit 提出了了 live 功能,简单说就是通过 ReplayKit 可以进行录屏直播。这对于苹果的手游直播行业有着很重要的意义。

首先给出视频地址和API文档

简单测试

弹出可以接收广播的服务列表

新建工程,然后加入ReplayKit.frameword

添加一个按钮,然后按钮点击事件弹出广播服务的列表:

- (IBAction)displayServiceViewController:(id)sender {
    [RPBroadcastActivityViewController loadBroadcastActivityViewControllerWithHandler:^(RPBroadcastActivityViewController * _Nullable broadcastActivityViewController, NSError * _Nullable error) {
        broadcastActivityViewController.delegate = self;
        [self presentViewController:broadcastActivityViewController animated:YES completion:nil];
    }];
}



Mobcrush

所以想要直播的游戏本身添加这样一个逻辑,弹出服务列表即可。而直播软件也只需要注册为直播服务,就可以直播任何支持的游戏,软件。国外最火最先支持的就是示例中左边的Mobcrush,官网,里面有手机游戏Tower Dash的直播,就是使用这个技术实现的,Tower Dash游戏直播页面为 - 这里这里,需要科学上网。

我录制了动态图展示:



动态图中可以看出支持摄像头录制,当然还有麦克风,这些已经满足了日常主播的基本需求。

国内

国内现在映客直播安装就直接有注册为广播服务,所以截图中列表里就有。我还安装了熊猫TV,虎牙直播,虎牙助手,虎牙手游。熊猫TV的主播权限还没有申请下来。虎牙手游貌似使用的也是这个技术,但是实现不一样,虎牙手游直接是在虎牙手游APP内部打开直播,提示成功之后,直接就进入了录屏模式,然后退出返回到手游界面开始游戏就可以。查看了虎牙直播平台,已经有主播使用了iPhone7进行王者荣耀直播,熊猫TV暂时还没有看到用iPhone直播手游的。

WWDC 2016

下面是观看WWDC16 记录的知识片段。

ReplayKit

新特性:

  • Apple TV support
  • Live Broadcasting 直播广播,这个很有用,就是要研究的直播功能
  • 可以记录 Face Time摄像头的内容,增强了麦克风记录API



录播功能

对于录播功能之前就已经有了一个典型的demo,可以直接看下面代码,放进原始功能的第一个viewcontroller里面就Ok了。



#import "ViewController.h"
#import <ReplayKit/ReplayKit.h>

static NSString *StartRecord = @"开始";
static NSString *StopRecord = @"结束";

#if TARGET_IPHONE_SIMULATOR
#define SIMULATOR 1
#elif TARGET_OS_IPHONE
#define SIMULATOR 0
#endif

#define AnimationDuration (0.3)


@interface ViewController () <RPPreviewViewControllerDelegate>
{

}
@property (nonatomic, strong)UIButton *btnStart;
@property (nonatomic, strong)UIButton *btnStop;
@property (nonatomic, strong)NSTimer *progressTimer;
@property (nonatomic, strong)UIProgressView *progressView;
@property (nonatomic, strong)UIActivityIndicatorView *activity;
@property (nonatomic, strong)UIView *tipView;
@property (nonatomic, strong)UILabel *lbTip;
@property (nonatomic, strong)UILabel *lbTime;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

}

- (void)viewDidAppear:(BOOL)animated {
    BOOL isVersionOk = [self isSystemVersionOk];

    if (!isVersionOk) {
        NSLog(@"系统版本需要是iOS9.0及以上才支持ReplayKit");
        return;
    }
    if (SIMULATOR) {
        [self showSimulatorWarning];
        return;
    }

    UILabel *lb = nil;
    CGSize screenSize = [UIScreen mainScreen].bounds.size;


    //标题
    lb = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 140)];
    lb.font = [UIFont boldSystemFontOfSize:32];
    lb.backgroundColor = [UIColor clearColor];
    lb.textColor = [UIColor blackColor];
    lb.textAlignment = NSTextAlignmentCenter;
    lb.numberOfLines = 3;
    lb.text = @"苹果ReplayKit Demo";
    lb.center =  CGPointMake(screenSize.width/2, 80);
    [self.view addSubview:lb];

    //创建按钮
    UIButton *btn = [self createButtonWithTitle:StartRecord andCenter:CGPointMake(screenSize.width/2 - 100, 200)];
    [self.view addSubview:btn];
    self.btnStart = btn;

    btn = [self createButtonWithTitle:StopRecord andCenter:CGPointMake(screenSize.width/2 + 100, 200)];
    [self.view addSubview:btn];
    self.btnStop = btn;
    [self setButton:btn enabled:NO];

    //loading指示
    UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 280, 80)];
    [self.view addSubview:view];
    view.backgroundColor = [UIColor redColor];
    view.layer.cornerRadius = 8.0f;
    view.center = CGPointMake(screenSize.width/2, 300);
    activity.center = CGPointMake(30, view.frame.size.height/2);
    [view addSubview:activity];
    [activity startAnimating];
    self.activity = activity;
    lb = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 280, 80)];
    lb.font = [UIFont boldSystemFontOfSize:20];
    lb.backgroundColor = [UIColor clearColor];
    lb.textColor = [UIColor blackColor];
    lb.layer.cornerRadius = 4.0;
    lb.textAlignment = NSTextAlignmentCenter;
    [view addSubview:lb];
    self.lbTip = lb;
    self.tipView = view;
    [self hideTip];


    //显示时间(用于看录制结果时能知道时间)
    lb = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 40)];
    lb.font = [UIFont boldSystemFontOfSize:20];
    lb.backgroundColor = [UIColor redColor];
    lb.textColor = [UIColor blackColor];
    lb.layer.cornerRadius = 4.0;
    NSDateFormatter * dateFormat = [[NSDateFormatter alloc] init] ;
    [dateFormat setDateFormat: @"HH:mm:ss"];
    NSString *dateString = [dateFormat stringFromDate:[NSDate date]];
    lb.text =  dateString;
    lb.center = CGPointMake(screenSize.width/2, screenSize.height/2 + 100);
    lb.textAlignment = NSTextAlignmentCenter;
    [self.view addSubview:lb];
    self.lbTime = lb;

    //进度条 (显示动画,不然看不出画面的变化)
    UIProgressView *progress = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, screenSize.width*0.8, 10)];
    progress.center = CGPointMake(screenSize.width/2, screenSize.height/2 + 150);
    progress.progressViewStyle = UIProgressViewStyleDefault;
    progress.progress = 0.0;
    [self.view addSubview:progress];
    self.progressView = progress;

    //计时器
    //更新时间
    [NSTimer scheduledTimerWithTimeInterval:1.0f
                                     target:self
                                   selector:@selector(updateTimeString)
                                   userInfo:nil
                                    repeats:YES];
}

#pragma mark - UI控件
//显示 提示信息
- (void)showTipWithText:(NSString *)tip activity:(BOOL)activity{
    [self.activity startAnimating];
    self.lbTip.text = tip;
    self.tipView.hidden = NO;
    if (activity) {
        self.activity.hidden = NO;
        [self.activity startAnimating];
    } else {
        [self.activity stopAnimating];
        self.activity.hidden = YES;
    }
}
//隐藏 提示信息
- (void)hideTip {
    self.tipView.hidden = YES;
    [self.activity stopAnimating];
}

//创建按钮
- (UIButton *)createButtonWithTitle:(NSString *)title andCenter:(CGPoint)center {

    CGRect rect = CGRectMake(0, 0, 160, 60);
    UIButton *btn = [[UIButton alloc] initWithFrame:rect];
    btn.layer.cornerRadius = 5.0;
    btn.layer.borderWidth = 2.0;
    btn.layer.borderColor = [[UIColor blackColor] CGColor];
    btn.backgroundColor = [UIColor lightGrayColor];
    btn.center = center;
    [btn setTitle:title forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(onBtnPressed:) forControlEvents:UIControlEventTouchDown];
    return btn;

}

//设置按钮是否可点击
- (void)setButton:(UIButton *)button enabled:(BOOL)enabled {
    if (enabled) {
        button.alpha = 1.0;
    } else {
        button.alpha = 0.2;
    }
    button.enabled = enabled;
}

//提示不支持模拟器
- (void)showSimulatorWarning {
    UIAlertAction *actionOK = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){

    }];
    UIAlertAction *actionCancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action){

    }];
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"ReplayKit不支持模拟器" message:@"请使用真机运行这个Demo工程" preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:actionCancel];
    [alert addAction:actionOK];

    [self presentViewController:alert animated:NO completion:nil];
}

//显示弹框提示
- (void)showAlert:(NSString *)title andMessage:(NSString *)message {
    if (!title) {
        title = @"";
    }
    if (!message) {
        message = @"";
    }
    UIAlertAction *actionCancel = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleCancel handler:nil];
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:actionCancel];
    [self presentViewController:alert animated:NO completion:nil];
}

//显示视频预览页面,animation=是否要动画显示
- (void)showVideoPreviewController:(RPPreviewViewController *)previewController withAnimation:(BOOL)animation {

    __weak ViewController *weakSelf = self;

    //UI需要放到主线程
    dispatch_async(dispatch_get_main_queue(), ^{

        CGRect rect = [UIScreen mainScreen].bounds;

        if (animation) {

            rect.origin.x += rect.size.width;
            previewController.view.frame = rect;
            rect.origin.x -= rect.size.width;
            [UIView animateWithDuration:AnimationDuration animations:^(){
                previewController.view.frame = rect;
            } completion:^(BOOL finished){

            }];

        } else {
            previewController.view.frame = rect;
        }

        [weakSelf.view addSubview:previewController.view];
        [weakSelf addChildViewController:previewController];


    });

}

//关闭视频预览页面,animation=是否要动画显示
- (void)hideVideoPreviewController:(RPPreviewViewController *)previewController withAnimation:(BOOL)animation {

    //UI需要放到主线程
    dispatch_async(dispatch_get_main_queue(), ^{

        CGRect rect = previewController.view.frame;

        if (animation) {

            rect.origin.x += rect.size.width;
            [UIView animateWithDuration:AnimationDuration animations:^(){
                previewController.view.frame = rect;
            } completion:^(BOOL finished){
                //移除页面
                [previewController.view removeFromSuperview];
                [previewController removeFromParentViewController];
            }];

        } else {
            //移除页面
            [previewController.view removeFromSuperview];
            [previewController removeFromParentViewController];
        }
    });
}

#pragma mark - 按钮 回调
//按钮事件
- (void)onBtnPressed:(UIButton *)sender {

    //点击效果
    sender.transform = CGAffineTransformMakeScale(0.8, 0.8);
    float duration = 0.3;
    [UIView animateWithDuration:duration
                     animations:^{
                         sender.transform = CGAffineTransformMakeScale(1.1, 1.1);
                     }completion:^(BOOL finish){
                         [UIView animateWithDuration:duration
                                          animations:^{
                                              sender.transform = CGAffineTransformMakeScale(1.0, 1.0);
                                          }completion:^(BOOL finish){ }];
                     }];

    NSString *function = sender.titleLabel.text;
    if ([function isEqualToString:StartRecord]) {
        [self startRecord];
    }
    else if ([function isEqualToString:StopRecord]) {
        [self stopRecord];
    }
}


- (void)startRecord {

    //    [self setButton:self.btnStart enabled:NO];

    NSLog(@"ReplayKit只支持真机录屏,支持游戏录屏,不支持录avplayer播放的视频");
    NSLog(@"检查机器和版本是否支持ReplayKit录制...");
    if ([[RPScreenRecorder sharedRecorder] isAvailable]) {
        NSLog(@"支持ReplayKit录制");
    } else {
        NSLog(@"!!不支持支持ReplayKit录制!!");
        return;
    }

    __weak ViewController *weakSelf = self;

    NSLog(@"%@ 录制", StartRecord);
    [self showTipWithText:@"录制初始化" activity:YES];


    [[RPScreenRecorder sharedRecorder] startRecordingWithHandler:^(NSError *error){
        NSLog(@"录制开始...");
        [weakSelf hideTip];
        if (error) {
            NSLog(@"错误信息 %@", error);
            [weakSelf showTipWithText:error.description activity:NO];
        } else {
            //其他处理
            [weakSelf setButton:self.btnStop enabled:YES];
            [weakSelf setButton:self.btnStart enabled:NO];

            [weakSelf showTipWithText:@"正在录制" activity:NO];
            //更新进度条
            weakSelf.progressTimer = [NSTimer scheduledTimerWithTimeInterval:0.05f
                                                                      target:self
                                                                    selector:@selector(changeProgressValue)
                                                                    userInfo:nil
                                                                     repeats:YES];
        }
    }];

}

- (void)stopRecord {
    NSLog(@"%@ 录制", StopRecord);

    [self setButton:self.btnStart enabled:YES];
    [self setButton:self.btnStop enabled:NO];

    __weak ViewController *weakSelf = self;
    [[RPScreenRecorder sharedRecorder] stopRecordingWithHandler:^(RPPreviewViewController *previewViewController, NSError *  error){


        if (error) {
            NSLog(@"失败消息:%@", error);
            [weakSelf showTipWithText:error.description activity:NO];
        } else {

            [weakSelf showTipWithText:@"录制完成" activity:NO];

            //显示录制到的视频的预览页
            NSLog(@"显示预览页面");
            previewViewController.previewControllerDelegate = weakSelf;

            //去除计时器
            [weakSelf.progressTimer invalidate];
            weakSelf.progressTimer = nil;

            [self showVideoPreviewController:previewViewController withAnimation:YES];
        }
    }];
}

#pragma mark - 视频预览页面 回调
//关闭的回调
- (void)previewControllerDidFinish:(RPPreviewViewController *)previewController {
    [self hideVideoPreviewController:previewController withAnimation:YES];
}

//选择了某些功能的回调(如分享和保存)
- (void)previewController:(RPPreviewViewController *)previewController didFinishWithActivityTypes:(NSSet <NSString *> *)activityTypes {

    __weak ViewController *weakSelf = self;
    if ([activityTypes containsObject:@"com.apple.UIKit.activity.SaveToCameraRoll"]) {

        dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf showAlert:@"保存成功" andMessage:@"已经保存到系统相册"];
        });
    }
    if ([activityTypes containsObject:@"com.apple.UIKit.activity.CopyToPasteboard"]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf showAlert:@"复制成功" andMessage:@"已经复制到粘贴板"];
        });
    }
}

#pragma mark - 计时器 回调

//改变进度条的显示的进度
- (void)changeProgressValue {
    float progress = self.progressView.progress + 0.01;
    [self.progressView setProgress:progress animated:NO];
    if (progress >= 1.0) {
        self.progressView.progress = 0.0;
    }
}

//更新显示的时间
- (void)updateTimeString {
    NSDateFormatter * dateFormat = [[NSDateFormatter alloc] init] ;
    [dateFormat setDateFormat: @"HH:mm:ss"];
    NSString *dateString = [dateFormat stringFromDate:[NSDate date]];
    self.lbTime.text =  dateString;
}

#pragma mark - 其他
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

//判断对应系统版本是否支持ReplayKit
- (BOOL)isSystemVersionOk {
    if ([[UIDevice currentDevice].systemVersion floatValue] < 9.0) {
        return NO;
    } else {
        return YES;
    }
}

@end

新博客文章地址:ReplayKit库,iOS原生直播神器

作者:zyq522376829 发表于2016/9/21 10:41:27 原文链接
阅读:377 评论:0 查看评论

答CsdnBlogger问-关于职业发展和团队管理问题

$
0
0


 本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!


问1:关于职业发展以及团队管理?(正能同學_

请问在二线城市的小公司里,普通Android开发者的职业发展应该怎么向管理层过渡(具有前端以及后台开发经验)?以及当团队中有Android技术方面比你更好的新人进来时,怎么管理?

答1:

你好,二线城市,一般指省会所在地,按照目前国家总体经济形势来看,还是不错的。
普通Android开发者,刚开始是需要自我提升的,努力做项目,和同事搞好关系,年轻不要气盛,尽量积累好人品

其次项目要多涉猎一些,像流行的框架多用用,最好自己分析下,不行就看看别人的理解,推荐几本书《Java编程思想》是必须看的,《Android内核剖析》和《深入理解Java虚拟机》可以帮你更好的认识Android和Java,买来如果看不懂,可以放着,过段时间再看
等你看的懂,驾轻就熟的时候,说明水平也达到了,在这之前都好好做项目,先做这门技术一万小时,满足此定律。
然后一般只在一家公司,不能学到更多东西,薪资也会受到非常大的限制,可以跳一跳,平均2年跳一回,差的1年多也可以,这个时候你拥有比较多的工作经验,同时也可以逐步向前端和后台深处学习,俗话说:一样通样样通,就是说一样说通了,其他道理都是相通的,会学的很快,这时设计模式也需要熟悉,最好可以做一些项目架构,同时形成自己的架构风格和管理风格,如果有大公司的就职经验更佳。

当你工作5年,一般是职业的一个阶段期,需要稳定下来,沉淀职业生涯,积累财富同时自己也该考虑成家,这时最好找一个有前途的公司,或者说跟自己性格吻合的,之前找到那是最好,工资水平处于当地中上等,最好有股份或者期权,以期待未来有更大的收获。

现在回答你第2个问题,如果团队中有新人进来,而且技术比你好-首先三人行必有我师,比你强这是好事,这样才可以互相学生和切磋,然而天下无不散的筵席,不是你先走就是他先走,关键是自己得到什么成长和锻炼,学到什么东西;只要你坚持做这一行,总有一天你能成为行业的顶尖,因为中途总有人转行去做其他职业,或者转型,都再所难免。所以不要担心这个问题,和他好好相处,取长补短,共同把公司项目做好,谁说的方案好听谁的,咱们做技术的不搞形式主义,事情做不好是最糟糕的,而且方案永远没有最好,只有更好,也方便你们培养一个上进的环境。而对于个人而言,如果已经是管理岗位,可以让对方在擅长的领域多发挥一些,鼓励他承担和挑战难题,一方面他的能力得到发挥,另一方面你的工作也会轻松许多,而且你也可以从中学到很多,用人之长,宽容大度;能管4个人,你就是主管,12个人就是经理,管的人能力越强,你个人的成就也会越大,千万要往大处想,不可斤斤计较。


问2:关于职业规划和未来发展的问题(提问者:alanjet

您好,我是一名在读大三学生,个人酷爱android开发,但由于自己当初没有选择读计算机专业,而是读了通信工程。很多编程方面的知识都是自学而来,android 自己玩了一年左右,因为要兼顾学业,目前只做过三个实际的项目,能力还是跟很多计算机专业科班出身的同学有差距。现在纠结一个问题,我是应该努力跨专业考一个计算机专业的研究生来补足自己的非科班出身造成的短板呢,还是应该去就业,哪怕是进不了大公司,也去积累工作经验也许更重要呢?很纠结,您给点意见和看法吧,谢谢您。

答2:

你好,前面两篇已经讲了兴趣和工作的事情,好在你刚大三,选择会多一些。举个小例子:之前一个同事大二就出来工作,直到毕业前夕,通过做外包,自己挣到人生第一个一百万,这只是个例,当然现在情况没有以前好了,即使通过做项目达到高级的状态,也很难有这样机会,除非有政府关系。

至于选工作还是读研的这个问题,不同人选择不同,如果你没有坚定的目标去工作,那还是选择读研吧。相当于你起点又高一些,而且读研一般一生就这一次机会,机会成本也比较高,第三如果你想准备大公司面试:一般要求研究生起,除非校招特别优秀,学院前三名那种,算法、计算机原理、单片机、基本的Java和Android知识、安全、通信等这块需要熟悉,可以去网上搜一下历年的面试题,大同小异(每年题目变化多端,不建议刷题,去了解背后的原理吧)。

就读研的专业来说,工作中见到通信工程来做软件开发的最多,计算机本专业的反而很少,因为自己是计算机专业的,而通信工程见到最多的是北京邮电大学和华中科技大学,相信这两所学校在计算机这块应该属于属一属二的专业吧。如果明确目标那就去准备,如果犹豫不决,就先一再二
当然时间成本可能会很高。如果大学时你能把上面讲的一些知识弄懂的话,那么可以参加明年9月份的校招,多去ACM上刷题,时间只有一年,不知道你能否把这些都学的比较纯熟,成为完全的技术男;一般情况下,这时也该准备读研了,按照自己的想法来。还有就是积累工作经验,看你是动手型还是理论型,如果动手能力比较强,去工作会有更多机会,理论能力比较强,读研会更好;但计算机专业比较特殊,学习的时候不多,去读读也是不错的,建议你去读研,考上最好,考不上自己努力过也不会再后悔,当然如果你一心想考那也是可以的,最倒霉的看到一个校友四年才考上,其中一年工作,人生很长,现在很短,许多选择做完就不能再回头,希望你做好自己的抉择。真不行看看开复老师的《世界因你而不同》讲的也是这个问题。

至于你现在酷爱Android开发,这跟计算机专业相关性不太大,比如软件工程、算法、Java等,主要是自成体系的一些东西,看看书-前面也有推荐,自己可以学的很好,加上自己的实践,因为了解到我的高中同学,读计算机研究生第一年往往在校学习理论,第二年实习、第三年论文,如果你的导师自己没有项目的话,一句话:主要靠自己。而且你也看到,研究生读起来性价比也比较高,只用读1年,但前不久看的一本书是哈工程的两位老师张国印 吴艳霞编写,那个书差的真是一塌糊涂,虽然前不久有人在微博上吐槽哈工程不如哈工大,那水平还真是不行,写书完全不用心,其他也不用期待了,所以读研最好选个好学校。

至于你已经做过三个项目,这已经比同龄人强很多,不能说计算机专业的就一定行,这个时候也面临实习的机会,如果实习应该找到工作问题不大,选择你心底最深的期待吧,工作还是读研,选择内心的真实呼唤。
PS-如果不懂,就自虐吧,比如去跑步,一个不熟悉的地方,等到自己累到死时,自然会知道内心的想法-这也是跌入低俗的办法,往往激起内心 的真实想法。


alanjet 谢谢您,听了您的一番话,我感触很多。我决定了去找工作,也许这不见得是一个一定正确的选择,但很多选择,做了才知道。听您的,遵从我内心最大的声音,也许我去不了大公司,但该我走的路,会拼命认真地走。没想到能与您这样的专家交流,真的很感谢您指点迷津。谢谢。


问3:关于职业规划,以及工作发展方向的问题(提问者:Stanny_Bing

你好,我目前是做Android开发的,算上实习期,目前开发了一年了,不算实习期的话,才毕业开始工作两个月的时间。最近,我慢慢地开始考虑我的未来规划,有两条路,一条是学习Java后台方向,Android和Java后台兼修,另一条是学习IOS方向,走移动端开发路线。但是做IOS需要考虑的一是开发设备的问题,二是语言的问题,在OC上我基本没什么了解了。我个人比较倾向兼修IOS,但是又比较纠结。请问,我该怎么去选择我的学习方向。
另外就是,我所在的公司做的项目重心在网页端,移动端的看重不大,还有就是,我们是给政府做软件,对界面要求不高,我做了好几个项目都是用的同一个项目复制出去的大体框架,版本也还停留在4.2上,很多高版本的东西没机会去涉及。我应该换么。还是再累计一两年经验,或者其他的什么方法。


答3:

你好,目前看你的情况,确实会比较纠结和迷茫,不过还好优势和劣势比较起来,差距比较大,也容易做出选择。 

        先说选择的方向,一般来说,个人建议选择自己优势去参与竞争,即去找工作,凭借你的劣势去找工作,根本找不到,现在公过来最好能直接上手做项目,不用教;而OC更像你的兴趣,但你要选择的是根身立命的技能,就不能因兴趣,而因优势,在发达国家如丹麦、瑞典除外,人家是全民收入基本相等,类似共产主义社会,也容易出现优质的人才,像我国竞争这么激烈,不凭优势,基本生活会很有问题。

就工作技能和兴趣来讲,一般选择技能来生活,兴趣业余培养,当你的兴趣已经优于你的技能所提供的财务价值时,那么就可以选择由兴趣产生

技能,或者其他第二技能或者叫作业务。所以你本身是学习Java的,又做Android一年,显然Android更容易找工作,而就今年的面试情况而言,IOS
求职者要远多于Android,就业也有优势,其次如果公司提供你学习后台的机会,那是最好,不过一般不过有,因为公司雇佣你的优势,而且只雇佣你的优势,如果要发挥比较全面,可以去创业公司,缺衣少药,野蛮生长,全靠自己一肩挑起,当然有些创业公司开出的薪水不比大公司低,显然对你的要求会更高,且行且观察吧,可以私下搭建一个个人网络,来实践你后端的技术,比如使用JFinal。
而对于未来,三五年之后,最好Android和IOS都会,这样你可以做到移动端总监,否则你只能是个主管或者经理,阻碍你成为一个部门的负责人,而这也正是我目前的现状,当然你如果没有那个想法,一心做好自己的事,把事做精当然也可以,是走技术方向的体现,刚我说的是走管理路线,
通常做技术的也仅有这两种路线可以选择;30岁前如果有去大公司的机会,尽量去,可以看的更全面,更立体,扩大视野,30岁后建议去创业公司,将自己的技术放大,拿到的期权或许三五年后,就值个几百上千万。因此,当你工作稳定之后,有些余钱,建议你更换开发设备,自己换Mac Pro和苹果手机,业余自己搞些苹果端开发,而公司通常不会给你换,大公司除外,像蘑菇街和支付宝入职送笔记本的。你学通Android后,IOS上手也会比较快,语言都是相通的,自己再做些项目,写写博客,逐步做积累,相信有一天,你会成为行业大牛。
最后,对于你目前的公司,一般国企和外包企业是不能选择的,学的东西少和职业生涯不稳定,对你将来益处较少;需要到一个重视移动端的公司,最好主要业务来源于移动端,比如百度地图,这样你的话语权会大些,薪水会高些,晋升也会快一些;一般情况下在小公司,要一两年换一份工作,因为原来的业务基本已经固化,除非规模较大,但那又是大公司了,你的技术提升有限;而且换工作也是提升薪水的有效方式,至少工作前五年,之后就需要稳定发展,不要轻易跳槽,而上面的问题也有讲过类似的话题,就不再过多阐述。


作者:liuxian13183 发表于2016/9/21 10:44:59 原文链接
阅读:314 评论:0 查看评论

从零学React Native之14 网络请求

$
0
0

通过HTTP或者HTTPS协议与网络侧服务器交换数据是移动应用中常见的通信方式。 node-fetch是RN推荐的请求方式。
React Native框架在初始化项目时, 引入了node-fetch包 (因为npm3把依赖全部摊平了,node-fetch就在node_modules目录下)

下面就是项目中引入的node-fetch的源码:

联网

联网分为发送请求和接受响应两步。分开来分析下。

发送请求

发送http/https gong细分一下共有6个步骤
1. 确定并准备请求地址与协议
2. 确定请求中的HTTP方法
3. 准备请求中药传输的消息头
4. 准备身份验证信息
5. 确定是否需要携带消息体/参数
6. 发出消息

我们以查询号码归属地这个地址演示下。

1. 确定并准备请求地址与协议

请求地址以http/https 开头,为了便于修改地址,通常将地址放在一个变量中。IOS9以上默认无法访问http请求, 具体参考后面的注意事项。

let url=`http://apis.baidu.com/showapi_open_bus/mobile/find`;

2. 确定请求中的HTTP方法

根据HTTP协议的设计初衷,不同的方法对资源有不同的操作方式:
+ PUT :增
+ DELETE :删
+ POST:改
+ GET:查

最常用的是GET和POST(实际上GET和POST都能办到增删改查)
如果不指定,默认的方法为GET,当前使用的api接口只支持get请求

let map={
     method:'GET'
};

3. 准备请求中药传输的消息头

接下来需要设置请求中需要传输的消息头。消息头分为两种,其中一种是协议规定的标准消息头;另一种是用户自定义的消息头。

let privateHeaders={
     'Private-Header':'values1', //自定义消息头
     //'Content-Type':"text/plain", //设置消息体格式,GET请求不需要设置
     //'User-Agent':'testAgent',// 如果不设置默认为okhttp/2.5.0
     'apikey':'9dc7ab2f8993b0b215ad8c550e1f4ebe' //百度账号的apikey
};
map.headers=privateHeaders; //加上自定义消息头
map.follow=20;//设置允许最大的重定向次数,0不允许重定向
map.timeout=0;//设置超时时间,0代表没有
map.size=0;//请求回应中消息体最大允许的长度,0没有限制

RN支持gzip/deflate格式编码,不需要对此进行设置。RN收到gzip/deflate格式编码会自动转换为普通格式交给开发者。
消息头是不区分大小写的,如果有大写字母,传输的时候回自动转换为小写。
key信息请自己注册。

4. 准备身份验证信息

某些Http请求需要加入身份验证信息,比如上面的apikey,有些时候在需要进行身份验证的时候需要两次HTTP请求来完成。

5. 确定是否需要携带消息体/参数

如果需要带消息体,可以通过body配置,GET请求不需要。

map.body=JSON.stringify({
       fistParam:'yourValue',
       secondParam:'yourValue'
});

POST请求参数是放在消息体中,而GET直接拼装在请求地址后面通过?间隔。

let url=`http://apis.baidu.com/showapi_open_bus/mobile/find`;
let params='num=13126939916';
let requestURL=url+"?"+params;

6. 发出消息

将上述5步联合起来就可以得到发送HTTP或HTTPS的请求片段。如下:

  componentDidMount() {
        let url=`http://apis.baidu.com/showapi_open_bus/mobile/find`;
        let params='num=13126939916';
        let requestURL=url+"?"+params;
        let map={
          method:'GET'
        };
        let privateHeaders={
          //  'Private-Header':'values1', //自定义消息头
          //  'Content-Type':"text/plain", //设置消息体格式,GET请求不需要设置
          //  'User-Agent':'testAgent',// 如果不设置默认为okhttp/2.5.0
            'apikey':'9dc7ab2f8993b0b215ad8c550e1f4ebe'
        };
         map.headers=privateHeaders; //加上自定义消息头
         map.follow=20;//设置允许最大的重定向次数,0不允许重定向
         map.timeout=0;//设置超时时间,0代表没有
         map.size=0;//请求回应中消息体最大允许的长度,0没有限制
         //map.body='this is body';
        fetch(requestURL,map)
            .then(
                (result)=>{
                    //接受回应消息处理.
                    console.log(result);
                }
            )
    }

注意事项 解决IOS9以上默认不能访问http请求

WWDC 15 提出的 ATS (App Transport Security) 是 Apple 在推进网络通讯安全的一个重要方式。在 iOS 9 和 OS X 10.11 中,默认情况下非 HTTPS 的网络访问是被禁止的。当然,因为这样的推进影响面非常广,作为缓冲,我们可以在 Info.plist 中添加 NSAppTransportSecurity 字典并且将NSAllowsArbitraryLoads 设置为 YES 来禁用 ATS。

找到info.plist文件添加NSAllowsArbitraryLoads

或者直接在xcode中打开ios项目进行配置。

接收响应

当请求成功,会返回一个存储有回应数据的对象

  1. type 字符串类型,记录请求类型
  2. url 请求地址
  3. status 响应码 200代表成功
  4. ok 布尔类型,记录是否成功。

fetch方法

fetch 返回的 then 方法有一个 response 参数,它是一个 Response 实例。 Response 有如下方法:

  • clone() - 复制一份response
  • error() - 返回一个与网络相关的错误
  • redirect() - 返回了一个可以重定向至某URL的response.
  • arrayBuffer() - 返回一个带有ArrayBuffer的Promise.
  • blob() - 返回一个带有Blob的Promise.
  • formData() - 返回一个带有FormData的Promise.
  • json() - 返回一个带有JSON格式对象的Promise.
  • text() - 返回一个带有文本的Promise.

下面就是完整的返回json的例子:

fetch(requestURL,map)
     .then((result)=>result.json()) // 返回带有json格式的Promise
     .catch((error)=>{   // 捕获错误
           console.log(error);  
     })
     .then((responseData)=>{   // 输出json数据
           console.log(responseData);
     });

完整的例子

    componentDidMount() {
        let url=`http://apis.baidu.com/showapi_open_bus/mobile/find`;
        let params='num=13126939916';
        let requestURL=url+"?"+params;
        let map={
          method:'GET'
        };
        let privateHeaders={
            'apikey':'9dc7ab2f8993b0b215ad8c550e1f4ebe'
        };
         map.headers=privateHeaders; //加上自定义消息头
         map.follow=20;//设置允许最大的重定向次数,0不允许重定向
         map.timeout=0;//设置超时时间,0代表没有
         map.size=0;//请求回应中消息体最大允许的长度,0没有限制
        fetch(requestURL,map)
            .then((result)=>result.json()) //.json()
            .catch((error)=>{
                console.log(error);
            })
            .then((responseData)=>{
                console.log(responseData);
            });
    }

当然也可以用ES2017的语法:

    async netWork() {
        let url=`http://apis.baidu.com/showapi_open_bus/mobile/find`;
        let params='num=13126939916';
        let requestURL=url+"?"+params;
        try {
            let response = await fetch(requestURL,{
                headers:{
                    'apikey':'9dc7ab2f8993b0b215ad8c550e1f4ebe'
                }
            });
            let responseJson = await response.json();
            console.log(responseJson);
            return responseJson;
        } catch (error) {
            console.error(error);
        }
    }

更多精彩请关注微信公众账号likeDev
这里写图片描述

作者:yulianlin 发表于2016/9/21 11:28:52 原文链接
阅读:275 评论:0 查看评论
Viewing all 5930 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>