精华内容
下载资源
问答
  • 就比如说是咱们最近打开过的word文档、excel文档或者其他的程序等等,都是会有记录的,这些历史记录存在的目的是为了让我们方便查询,方便再次打开,但是却无形之中透露了我们的私密,那么是否有办法可以删除这些...

    我想,大家在使用win7原版系统下载 iso的时候一定会涉及到一些隐私的文件吧?另一方面来说,想必很多朋友都知道,win7原版系统下载 iso有一个十分人性化的设置,就比如说是咱们最近打开过的word文档、excel文档或者其他的程序等等,都是会有记录的,这些历史记录存在的目的是为了让我们方便查询,方便再次打开,但是却无形之中透露了我们的私密,那么是否有办法可以删除这些历史记录呢?下面,小编就来告诉大家应该如何操作。

    1.首先,咱们同时按下键盘上的win+r打开电脑的运行窗口,然后在窗口中输入gpedit.msc并单击回车,这样就可以打开电脑的组策略编辑器窗口了。

    2.在打开的组策略编辑器窗口中,咱们在左侧菜单栏中依次点击展开“用户配置”-“管理模板”-“任务栏和开始菜单”,之后咱们再来看右侧窗口,就可以找到“不保留最新打开文档的历史”和“退出时清除最近打的文档的记录”两个选项了。

    7e92fd800fdaec21581f7b44033e00d8.png

    3.咱们分别双击打开这两项,在弹出来的窗口中,咱们将默认的设置更改为已启用,然后点击确定保存设置即可。

    展开全文
  • 有时我们在APP里会有浏览的需求,常见的文件有Word、Excel、ppt、pdf等格式的文件,这里我选用了腾讯的TBS浏览服务。在这里记录一下,方便以后使用,有需要的朋友也可以参考一下。下面就来看一下怎样集成TBS。 先来...

    有时我们在APP里会有浏览的需求,常见的文件有Word、Excel、ppt、pdf等格式的文件,这里我选用了腾讯的TBS浏览服务。在这里记录一下,方便以后使用,有需要的朋友也可以参考一下。下面就来看一下怎样集成TBS。

    先来张效果图看下

    首先下载腾讯浏览服务SDK,下载地址:https://x5.tencent.com/tbs/sdk.html,接入文档地址:https://x5.tencent.com/tbs/guide/sdkInit.html,下载完导入jar包和.so文件。

    在app的build.gradle里配置

    android {
        compileSdkVersion 28
        defaultConfig {
            ……
            //X5兼容64位手机
            ndk {
                abiFilters "armeabi", "armeabi-v7a", "x86", "mips"
            }
        }
        buildTypes {
            ……
        }
    }
    
    dependencies {
        ……
        //网络请求okhttp3
        implementation 'com.squareup.okhttp3:okhttp:3.12.0'
        //Retrofit2库
        implementation 'com.squareup.retrofit2:retrofit:2.3.0'
        //gson解析
        implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
        //TBS腾讯浏览服务
        implementation files('libs/tbs_sdk_thirdapp_v4.3.0.1148_43697_sharewithdownloadwithfile_withoutGame_obfs_20190805_175505.jar')
    }

    在AndroidManifest.xml里加入如下权限

        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
        <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

     在Application 的onCreat()里加入

    //增加这句话
    QbSdk.initX5Environment(this,null);

     自定义一个现实文件的ViewSuperFileView

    package com.wjy.project.mytbs;
    
    import android.content.Context;
    import android.os.Bundle;
    import android.os.Environment;
    import android.text.TextUtils;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.widget.FrameLayout;
    import android.widget.LinearLayout;
    
    import com.tencent.smtt.sdk.TbsReaderView;
    
    import java.io.File;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    
    /**
     * Created by wjy on 2020/2/11
     * 自定义文件打开阅读View
     */
    public class SuperFileView extends FrameLayout implements TbsReaderView.ReaderCallback {
    
        private static String TAG = "SuperFileView";
        private Context context;
        private TbsReaderView mTbsReaderView;
        private OnGetFilePathListener mOnGetFilePathListener;
    
        public OnGetFilePathListener getOnGetFilePathListener() {
            return mOnGetFilePathListener;
        }
    
        public void setOnGetFilePathListener(OnGetFilePathListener mOnGetFilePathListener) {
            this.mOnGetFilePathListener = mOnGetFilePathListener;
        }
    
        public SuperFileView(@NonNull Context context) {
            this(context, null, 0);
        }
    
        public SuperFileView(@NonNull Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public SuperFileView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            this.context = context;
            mTbsReaderView = new TbsReaderView(context, this);
            this.addView(mTbsReaderView, new LinearLayout.LayoutParams(-1, -1));
        }
    
        @Override
        public void onCallBackAction(Integer integer, Object o, Object o1) {
            Log.e(TAG,"****************************************************" + integer);
        }
    
        /***
         * 获取File路径
         */
        public interface OnGetFilePathListener {
            void onGetFilePath(SuperFileView mSuperFileView);
        }
    
        /**
         * 展示
         */
        public void show() {
            if(mOnGetFilePathListener!=null){
                mOnGetFilePathListener.onGetFilePath(this);
            }
        }
    
        /**
         * 显示文件的界面,退出界面以后需要销毁,否则再次加载文件无法加载成功,会一直显示加载文件进度条。
         */
        public void onStopDisplay() {
            if (mTbsReaderView != null) {
                mTbsReaderView.onStop();
            }
        }
    
        /**
         * 显示文件
         */
        public void displayFile(File mFile){
            if (mFile != null && !TextUtils.isEmpty(mFile.toString())){
                //增加下面一句解决没有TbsReaderTemp文件夹存在导致加载文件失败
                String bsReaderTemp = "/storage/emulated/0/TbsReaderTemp";//SD卡下面文件路径
                File bsReaderTempFile =new File(bsReaderTemp);
                if (!bsReaderTempFile.exists()){//如果SD卡下不存在TbsReaderTemp文件夹
                    Log.e(TAG,"准备在SD卡下创建TbsReaderTemp文件夹!");
                    boolean mkdir = bsReaderTempFile.mkdir();
                    if(!mkdir){
                        Log.e(TAG,"创建/storage/emulated/0/TbsReaderTemp失败!!!!!");
                    }
                }
    
                //加载文件
                Bundle localBundle = new Bundle();
                Log.e(TAG,"mFile.toString()="+mFile.toString());
                localBundle.putString("filePath", mFile.toString());
                localBundle.putString("tempPath", Environment.getExternalStorageDirectory() + "/" + "TbsReaderTemp");
    
                if (mTbsReaderView == null){
                    mTbsReaderView = new TbsReaderView(context,this);
                }
                boolean bool = mTbsReaderView.preOpen(getFileType(mFile.toString()), false);
                if (bool){
                    mTbsReaderView.openFile(localBundle);
                }
            }else {
                Log.e(TAG,"文件路径无效!");
            }
        }
    
        /***
         * 获取文件类型
         *
         * @param paramString
         * @return
         */
        private String getFileType(String paramString){
            String str = "";
            if (TextUtils.isEmpty(paramString)) {
                Log.e(TAG, "paramString---->null");
                return str;
            }
            Log.e(TAG, "paramString:" + paramString);
    
            int i = paramString.lastIndexOf('.');
            if (i <= -1) {
                Log.e(TAG, "i <= -1");
                return str;
            }
    
            str = paramString.substring(i + 1);
            Log.e(TAG, "paramString.substring(i + 1)------>" + str);
            return str;
        }
    }
    

    之后就可以在使用了,新建打开文件的Activity页面FileDisplayActivity

    package com.wjy.project.mytbs;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.text.TextUtils;
    import android.util.Log;
    import android.view.View;
    import android.widget.TextView;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    import androidx.annotation.Nullable;
    import okhttp3.ResponseBody;
    import retrofit2.Call;
    import retrofit2.Callback;
    import retrofit2.Response;
    
    /**
     * Created by wjy on 2020/2/11
     * 文件打开阅读页面
     */
    public class FileDisplayActivity extends Activity {
    
        SuperFileView mSuperFileView;
        String filePathUrl;
        private TextView tv_progress;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_filedisplay);
            filePathUrl = getIntent().getStringExtra("filePath");
            Log.e("tag","filePathUrl="+filePathUrl);
            init();
        }
    
        private void init(){
            tv_progress = findViewById(R.id.tv_progress);
            mSuperFileView = findViewById(R.id.mSuperFileView);
            mSuperFileView.setOnGetFilePathListener(new SuperFileView.OnGetFilePathListener() {
                @Override
                public void onGetFilePath(SuperFileView mSuperFileView) {
                    openFile(filePathUrl,mSuperFileView);//获取文件路径并打开文件
                }
            });
            mSuperFileView.show();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.e("tag","FileDisplayActivity-->onDestroy");
            if (mSuperFileView != null) {
                mSuperFileView.onStopDisplay();
            }
        }
    
        /**
         * 打开文件文件
         * @param filePathUrl
         * @param mSuperFileView
         */
        private void openFile(final String filePathUrl,final SuperFileView mSuperFileView){
            //从缓存获取文件
            File cacheFile = FileTools.getCacheFile(filePathUrl);
            if (cacheFile.exists()) {//如果文件存在
                if (cacheFile.length() <= 0) {
                    Log.e("tag", "删除空文件!!");
                    cacheFile.delete();
                    return;
                }
                Log.e("tag","如果文件存在"+cacheFile.exists());
                mSuperFileView.displayFile(cacheFile);//直接打开文件
            }else {
                tv_progress.setVisibility(View.VISIBLE);
                //如果文件不存在,则在网络先下载然后再打开
                requestDownLoadFile();
            }
        }
    
        /**
         * 从网络下载文件
         */
        private void requestDownLoadFile(){
            //网络下载
            LoadFileModel.loadFile(filePathUrl, new Callback<ResponseBody>() {
                @Override
                public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                    Log.e("tag", "下载文件-->onResponse");
                    InputStream is = null;
                    byte[] buf = new byte[2048];
                    int len = 0;
                    FileOutputStream fos = null;
    
                    try {
                        ResponseBody responseBody = response.body();
                        is = responseBody.byteStream();
                        long total = responseBody.contentLength();
    
                        File file1 = FileTools.getCacheDir();
                        if (!file1.exists()) {
                            file1.mkdirs();
                            Log.e("tag", "创建缓存目录: " + file1.toString());
                        }
    
                        File fileN = FileTools.getCacheFile(filePathUrl);
                        Log.e("tag", "创建缓存文件: " + fileN.toString());
                        if (!fileN.exists()) {
                            boolean mkdir = fileN.createNewFile();
                        }
                        fos = new FileOutputStream(fileN);
                        long sum = 0;
                        while ((len = is.read(buf)) != -1) {
                            fos.write(buf, 0, len);
                            sum += len;
                            int progress = (int) (sum * 1.0f / total * 100);
                            Log.e("tag", "写入缓存文件" + fileN.getName() + "进度: " + progress);
                        }
                        fos.flush();
                        Log.e("tag", "文件下载成功,准备展示文件。");
                        tv_progress.setVisibility(View.GONE);
                        mSuperFileView.displayFile(fileN);
                    } catch (Exception e) {
                        Log.e("tag", "文件下载异常 = " + e.toString());
                    } finally {
                        try {
                            if (is != null)
                                is.close();
                        } catch (IOException e) {
                        }
                        try {
                            if (fos != null)
                                fos.close();
                        } catch (IOException e) {
                        }
                    }
                }
    
                @Override
                public void onFailure(Call<ResponseBody> call, Throwable t) {
                    Log.e("tag", "文件下载失败");
                    File file = FileTools.getCacheFile(filePathUrl);
                    if (!file.exists()) {
                        Log.e("tag", "删除下载失败文件");
                        file.delete();
                        requestDownLoadFile();//重新下载
                    }
                }
            });
        }
    }
    

    布局文件activity_filedisplay.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">
    
        <com.wjy.project.mytbs.SuperFileView
            android:id="@+id/mSuperFileView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
        <TextView
            android:id="@+id/tv_progress"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#E7E7E7"
            android:text="文件加载中"
            android:textColor="#999999"
            android:textSize="18sp"
            android:gravity="center"
            android:visibility="gone"/>
    
    </RelativeLayout>

    里面用到的一个工具类FileTools

    package com.wjy.project.mytbs;
    
    import android.os.Environment;
    import android.text.TextUtils;
    import android.util.Log;
    
    import java.io.File;
    
    /**
     * Created by wjy on 2020/2/11
     * 文件工具类
     */
    public class FileTools {
    
        public static String pathName = Environment.getExternalStorageDirectory().getAbsolutePath() + "/TbsReaderTemp/";
    
        /***
         * 获取缓存目录
         * @return
         */
        public static File getCacheDir() {
            return new File(pathName);
        }
    
        /***
         * 绝对路径获取缓存文件
         * @param url
         * @return
         */
        public static File getCacheFile(String url) {
            File cacheFile = new File(pathName + getFileName(url));
            Log.e("tag", "缓存文件 = " + cacheFile.toString());
            return cacheFile;
        }
    
        /***
         * 根据链接获取文件名(带类型的),具有唯一性
         * @param url
         * @return
         */
        private static String getFileName(String url) {
            String fileName = Md5Tool.hashKey(url) + "." + getFileType(url);
            Log.e("tag","fileName="+fileName);
            return fileName;
        }
    
        /***
         * 获取文件类型
         * @param paramString
         * @return
         */
        private static String getFileType(String paramString) {
            String str = "";
            if (TextUtils.isEmpty(paramString)) {
                Log.e("tag", "paramString---->null");
                return str;
            }
            Log.e("tag","paramString:"+paramString);
            int i = paramString.lastIndexOf('.');
            if (i <= -1) {
                Log.e("tag","i <= -1");
                return str;
            }
    
            str = paramString.substring(i + 1);
            Log.e("tag","paramString.substring(i + 1)------>"+str);
            return str;
        }
    }
    

    用到的MD5工具,文件下载的名字唯一性

    package com.wjy.project.mytbs;
    
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    
    /**
     * Created by wjy on 2020/2/11.
     */
    
    public class Md5Tool {
    
        private static String bytesToHexString(byte[] bytes) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < bytes.length; i++) {
                String hex = Integer.toHexString(0xFF & bytes[i]);
                if (hex.length() == 1) {
                    sb.append('0');
                }
                sb.append(hex);
            }
            return sb.toString();
        }
    
        public static String hashKey(String key) {
            String hashKey;
            try {
                final MessageDigest mDigest = MessageDigest.getInstance("MD5");
                mDigest.update(key.getBytes());
                hashKey = bytesToHexString(mDigest.digest());
            } catch (NoSuchAlgorithmException e) {
                hashKey = String.valueOf(key.hashCode());
            }
            return hashKey;
        }
    
    }
    

    下面的本例子中用到的网络请求 okhttp3:LoadFileModel

    package com.wjy.project.mytbs;
    
    import android.text.TextUtils;
    
    import okhttp3.ResponseBody;
    import retrofit2.Call;
    import retrofit2.Callback;
    import retrofit2.Retrofit;
    import retrofit2.converter.gson.GsonConverterFactory;
    
    /**
     * Created by wjy on 2020/2/11.
     */
    
    public class LoadFileModel {
    
    
        public static void loadFile(String url, Callback<ResponseBody> callback) {
    
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("https://www.baidu.com/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
            LoadFileApi mLoadFileApi = retrofit.create(LoadFileApi.class);
            if (!TextUtils.isEmpty(url)) {
                Call<ResponseBody> call = mLoadFileApi.loadPdfFile(url);
                call.enqueue(callback);
            }
    
        }
    }
    
    package com.wjy.project.mytbs;
    import okhttp3.ResponseBody;
    import retrofit2.Call;
    import retrofit2.http.GET;
    import retrofit2.http.Url;
    
    /**
     * Created by wjy on 2020/2/11.
     */
    
    public interface LoadFileApi {
    
        @GET
        Call<ResponseBody> loadPdfFile(@Url String fileUrl);
    
    }
    

    到此 整个例子就结束了。

    源码地址:

    git:https://gitee.com/AlaYu/MyTBS

    CSDN://download.csdn.net/download/u013184970/12154456 

    展开全文
  • SimpleWord 简词开发记录笔记

    千次阅读 2016-08-16 16:20:49
    删除 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder( this ) .setSmallIcon(R.drawable.notification_icon) .setContentTitle( "My notification" ) .setContentText( "Hello ...

    2015-8-6 15:07:54

    词库

    找不到词库,暂时找到一个带音标和释义的考研单词excel(估计是好几年前的大纲词汇),就先用这个吧。
    excel不能显示音标的话,还得下载字体TOPhonetic.ttf。

    数据库

    excel导入SQLite
    试了几个可视化工具,就SQLiyeStudio比较满意,也没有乱码。
    开始时把excel另存为.csv文件,系统的分隔符是逗号,但是去控制面板改了,excel导出时还是逗号,不知道为什么,可能需要重启电脑吧,懒得重启。竟然忘记replace了!所以excel → 制表符分隔的txt → replace分隔符,就ok了^_^

    优化——延迟切换fragment

    public void switchContent(Fragment fragment) {
        contentFragment = fragment;
        getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment).commit();
    //  sm.showContent();
        Handler h = new Handler();
        h.postDelayed(new Runnable() {
            public void run() {
                sm.showContent();
            }
        }, 50);
    }
    

    2015-8-10

    单词本界面想做成卡片来滑动

    RecyclerView

    关于引入V7包:http://wp.aesean.com/?p=185

    2015-8-11

    1. 继承RecyclerView.Adapter出现The hierarchy of the type ViewHolder is inconsistent,因为菜单用的SlidingMenu,support-v4包可能不是最新的,将SlidingMenu的libs下的v4包替换成最新的就可以了。
    2. Call requires API level 21 (current min is 17): android.content.Context#getDrawable
      解决

      ContextCompat.getDrawable(this, R.drawable.your_drawable)

    2015-8-12

    在actionbar添加spinner下拉列表

    1.actionbar.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_vertical"  
    android:orientation="horizontal" >  
    
        <TextView  
            android:id="@+id/action_bar_title"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"          
            android:text="下拉列表" />  
    
        <!-- 下拉列表  -->  
        <Spinner  
            android:id="@+id/action_bar_spinner"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            >  
        </Spinner>  
    
    </LinearLayout>  
    

    2.在activity的onCreate()或fragment的onCreateView()中添加代码,初始化下拉列表数据

    //使自定义的普通View能在title栏显示,actionBar.setCustomView能起作用
    getActivity().getActionBar().setDisplayShowCustomEnabled(true); 
    //初始化下拉列表
    View actionbarLayout = view.inflate(activity,R.layout.actionbar, null);  
    mActionbarSpinner = (Spinner) actionbarLayout.findViewById(R.id.actionbar_spinner);  
    
    //初始化下拉列表数据
    //方法一  
    initSpinnerMethod1();  
    //方法二  
    //initSpinnerMethod2();  
    
    //事件监听  
    mActionbarSpinner.setOnItemSelectedListener(new SpinnerItemSelectedListener());  
    
    //在Bar上显示定制view  
    actionBar.setCustomView(actionbarLayout); 
    

    初始化下拉列表数据:
    (1) 在strings.xml添加数组

    <string-array name="spinner_list" >  
    <item>item1</item>  
    <item>item2</item>  
    <item>item3</item>   
    </string-array>  
    

    代码:

    private void initSpinnerMethod1() {  
         String[] mItems = getResources().getStringArray(R.array.spinner_list);  
         ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(activity,android.R.layout.simple_spinner_item, mItems);  
         spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);  
         mActionbarSpinner.setAdapter(spinnerAdapter);  
    }  
    

    (2)在代码中添加数组

    private List<String> getData(){          
        List<String> data = new ArrayList<String>();  
        data.add("item1");  
        data.add("item2");  
        data.add("item3");          
        return data;  
    }  
    
    private void initSpinnerMethod2()  {  
        mActionbarSpinner.setAdapter(  
                new ArrayAdapter<String>(activity,   
                        android.R.layout.simple_expandable_list_item_1,getData()));    
    }  
    

    3.监听action_bar的spinner的item选择事件

    private class SpinnerItemSelectedListener implements OnItemSelectedListener {  
    
        @Override  
        public void onItemSelected(AdapterView<?> arg0, View view, int position,long arg3) {  
             String str= arg0.getItemAtPosition(position).toString();  
             Toast.makeText(MainActivity.this, "你选择的是:"+str, 2000).show();             
        }  
    
        @Override  
        public void onNothingSelected(AdapterView<?> arg0) {}  
    }  
    

    同一个activity的不同fragment显示不同的actionbar

    在单词本界面的actionbar想显示一个spinner下拉选择单词本(暂时未加入切换单词本的功能),在WordBookFragment的onCreateView()里设置自定义actionbar

    getActivity().getActionBar().setDisplayShowCustomEnabled(true);
    

    别的fragment里设置

    getActivity().getActionBar().setDisplayShowCustomEnabled(false);
    

    或者加载另外的view。

    不同fragment的标题

    分别在onCreateView()里设置

    getActivity().setTitle("标题");
    

    若写成

    getActivity().getActionBar().setTitle("标题");    
    

    会将所有fragment设置成同一标题

    在fragment里获取actionbar

    cannot convert from android.support.v7.app.ActionBar to android.app.ActionBar

    ActionBar actionBar = getActivity.getActionBar();
    

    使用quick fix:

    android.app.ActionBar actionBar;  
    

    2015-8-18

    控件左右对齐

    使用RelativeLayout

    android:layout_alignParentRight="true"
    

    2015-8-19

    判断程序是否首次运行

    使用SharedPreferences,在onCreate()里:

    SharedPreferences preferences;  
    Editor editor;  
    if (preferences.getBoolean("firststart", true)) {  //获取boolean值,可为缺省值,若缺省,则返回参数二的值
        editor = preferences.edit();  
        editor.putBoolean("firststart", false);  //若是首次,则改为false
        editor.commit();  //提交
    }  
    

    2015-8-20

    状态栏通知

    可删除

    NotificationCompat.Builder mBuilder =
        new NotificationCompat.Builder(this)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!");
    // Creates an explicit intent for an Activity in your app
    Intent resultIntent = new Intent(this, ResultActivity.class);
    // The stack builder object will contain an artificial back stack for the
    // started Activity.
    // This ensures that navigating backward from the Activity leads out of
    // your application to the Home screen.
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    // Adds the back stack for the Intent (but not the Intent itself)
    stackBuilder.addParentStack(ResultActivity.class);
    // Adds the Intent that starts the Activity to the top of the stack
    stackBuilder.addNextIntent(resultIntent);
    PendingIntent resultPendingIntent =
            stackBuilder.getPendingIntent(
                0,
                PendingIntent.FLAG_UPDATE_CURRENT
            );
    mBuilder.setContentIntent(resultPendingIntent);
    NotificationManager mNotificationManager =
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    // mId allows you to update the notification later on.
    mNotificationManager.notify(mId, mBuilder.build());
    

    常驻状态栏(显示在“进行中/ongoing”)

    Notification

    • 低于API 11

      notification.flags |= Notification.FLAG_NO_CLEAR;
      
    • 高于API 11 或者 使用Android Support Library

      //获取状态通知栏管理
      NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 
      //实例化通知栏构造器
      NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);  
      mBuilder.setSmallIcon(R.drawable.ic_launcher);//设置通知小图标
          .setContentTitle("标题")  //设置通知栏标题
          .setContentText("内容")   //设置通知栏显示内容
          .setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL)) //设置通知栏点击意图
          .setNumber(number) //设置通知集合的数量
          .setTicker("通知来啦") //通知首次出现在通知栏,带上升动画效果的
          .setWhen(System.currentTimeMillis())//通知产生的时间,会在通知信息里显示,一般是系统获取到的时间
          .setPriority(Notification.PRIORITY_DEFAULT) //设置该通知优先级
          .setAutoCancel(true)//设置这个标志当用户单击面板就可以让通知将自动取消  
          .setOngoing(false)//ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)
          .setDefaults(Notification.DEFAULT_VIBRATE)//向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合
      //Notification.DEFAULT_ALL  Notification.DEFAULT_SOUND 添加声音 // requires VIBRATE permission
      

    service前台服务

    在service里

    startForeground(mID, notification);     //设置前台服务
    

    关于服务service

    • 启动service的时候,onCreate方法只有第一次会调用,onStartCommandonStart(已被淘汰)每次都被调用。onStartCommand会告诉系统如何重启服务,如判断是否异常终止后重新启动,在何种情况下异常终止。
    • 2.0 API level之后,实现onStart等同于重写onStartCommand并返回START_STICKY 。
    • 2.0 API level之后,onStart()方法被onStartCommand()取代了。
    • 必须在AndroidManifest.xml中注册

      <service android:name="完整包名.ServiceNotification" />
      

    启动服务

    mContext.startService(intent);
    

    关闭服务

    mContext.stopService(intent);
    

    保存、恢复spinner的选中项

    保存

    onItemSelected()

    int userChoice = spinner.getSelectedItemPosition();
    SharedPreferences sharedPref = getSharedPreferences("FileName",0);
    SharedPreferences.Editor prefEditor = sharedPref.edit();
    prefEditor.putInt("userChoiceSpinner",usersChoice);
    prefEditor.commit();
    

    恢复

    SharedPreferences sharedPref = getSharedPreferences("FileName",MODE_PRIVATE);
    int spinnerValue = sharedPref.getInt"userChoiceSpinner",-1);
    if(spinnerValue != -1) 
        spinner.setSelection(spinnerValue);
    

    关于Activity生命周期该做的事情

    onCreate()

    • 使用onCreate()初始化你的Activity:创建UI、为类的变量分配引用、绑定数据到控件、创建Service和线程。
    • 为避免快速的创建和销毁对象引发额外的垃圾回收,如果你的应用程序正常创建一套对象,建议它们在onCreate()中创建,因为在Activity的生命周期中它只被调用一次。
    • onCreate()里面尽量少做事情,避免程序启动太久都看不到界面。

    onResume()

    当从Paused状态恢复activity时,系统会调用onResume()方法。
    系统每次调用onResume()时,activity都处于前台,包括第一次创建的时候。所以,应该实现onResume()来初始化那些在onPause方法里面释放掉的组件,并执行那些activity每次进入Resumed state都需要的初始化动作 (例如开始动画与初始化那些只有在获取用户焦点时才需要的组件)

    onPause()

    • 不应使用onPause()来保存用户改变的数据 (例如填入表格中的个人信息) 到永久存储(File或者DB)上。仅仅当确认用户期待那些改变能够被自动保存的时候(例如正在撰写邮件草稿),才把那些数据存到永久存储 。
    • 避免在onPause()时执行CPU-intensive 的工作,例如写数据到DB,因为它会导致切换到下一个activity变得缓慢(应该把那些heavy-load的工作放到onStop()去做)。
    • 如果activity实际上是要被Stop,为了切换的顺畅应减少在OnPause()方法里面的工作量。
    • 停止动画或者是其他正在运行的操作,那些都会导致CPU的浪费.
    • 提交在用户离开时期待保存的内容(例如邮件草稿).
    • 释放系统资源,例如broadcast receivers, sensors (比如GPS), 或者是其他任何会影响到电量的资源。

    onStop()

    • 当activity调用onStop()方法, activity不再可见,并且应该释放那些不再需要的所有资源。一旦activity停止了,系统会在需要内存空间时摧毁它的实例。极端情况下,系统会直接杀死我们的app进程,并不执行activity的onDestroy()回调方法, 因此我们需要使用onStop()来释放资源,从而避免内存泄漏。
      所以我们应该使用onStop()来执行那些耗时的释放资源的操作,例如往数据库写信息。
      -无论什么原因导致activity停止,系统总是会在onStop()之前调用onPause()方法。

    onDestroy()

    大多数app并不需要实现这个方法,因为局部类的references会随着activity的销毁而销毁,并且我们的activity应该在onPause()onStop()中执行清除activity资源的操作。然而,如果activity含有在onCreate调用时创建的后台线程,或者是其他有可能导致内存泄漏的资源,则应该在OnDestroy()时进行资源清理,杀死后台线程。

    除非程序在onCreate()方法里面就调用了finish()方法,系统通常是在执行了onPause()onStop()之后再调用onDestroy()。在某些情况下,例如我们的activity只是做了一个临时的逻辑跳转的功能,它只是用来决定跳转到哪一个activity,这样的话,需要在onCreate里面调用finish方法,这样系统会直接调用onDestory,跳过生命周期中的其他方法。

    与Activity生命周期结合的应用场景

    • 与广播(Broadcast)结合
      onResume注册广播(registerLinstener),在onPause注销广播(unregisterLinstener)。
      例如:做”摇一摇”功能(传感器)、监听网络变化,就可以在onResume中注册监听,在onPause里注销掉,已节省资源提高效率。
    • 与服务(Service)结合
      onStartCommand绑定服务(bindService),在onStop中取消绑定(unbindService)。
      例如:需要通过Service定时更新UI上的数据,而Activity的可见周期在onStartonStop之间,那么就可以再onStart时启动服务,在onStop时停止服务。为了节约系统资源,除了提高用户体验以外,开发人员应尽可能的优化程序。
    • 与Cursor结合
      使用managedQuery让Activity帮你管理Cursor的生命周期,不用自己去close。
    • 释放资源
      可以在onDestory中释放一些资源。比如可以在onDestory时调用MediaPlayer的release。

    AlarmManager定时启动Service

    private static AlarmManager am;
    private static PendingIntent pendingIntent;
    /**
     * 使用 AlarmManager 来 定时启动服务
     */
    public static void startPendingIntent(Context context) {
        am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, MyService.class);//启动示例Service
        pendingIntent = PendingIntent.getService(context, 0, intent, 0);
        long interval = DateUtils.MINUTE_IN_MILLIS * 30;// 30分钟一次
        long firstWake = System.currentTimeMillis() + interval;
        am.setRepeating(AlarmManager.RTC, firstWake, interval, pendingIntent);
    }
    public static void stopPendingIntent() {
        if (pendingIntent != null) {
            if ( am == null) {
                am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
            }
            am.cancel(pendingIntent);
            pendingIntent.cancel();
        }
    };
    

    参考:android 使用AlarmManager定时启动service

    Timer与AlarmManager的区别

    通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,但此时并没有运行,它需要CPU时间片。一旦得到CPU时间片,就会执行run()方法。run()的方法体称为线程体,它包含了要执行的这个线程的内容,run()方法运行结束,此线程也随即终止。

    Android 平台上常用的定时器主要有两个:

    • Java的Timer
    • Android的AlarmManager

    Timer

    Java的Timer类可以用来计划需要循环执行的任务。

    简单的说,一个Timer内部封装装了“一个Thread”和“一个TimerTask队列”,这个队列按照一定的方式将任务排队处理。封装的ThreadTimer的构造方法调用时被启动,这个Threadrun方法按照条件去循环这个TimerTask队列,然后调用TimerTaskrun方法。

    但是,如果CPU进入了休眠状态,那么这个thread将会因为失去CPU时间片而阻塞,从而造成我们需要的定时任务失效。上述定时任务失效的场景分析:假设定时任务的条件是到了时间xx:yy才能执行,但由于cpu休眠造成线程阻塞的关系,当前系统时间超过了这个时间,即便CPU从终端中恢复了,那么由于条件不满足,定时任务在这一次自然就失效了。

    解决方案:它需要用WakeLock让CPU保持唤醒状态。但这样会大量消耗手机电量,大大减短手机待机时间。这种方式不能满足我们的需求。

    注:TimerTask实现Runnable接口,但它的run方法只是简单的在Timer中封装的Thread中被调用,并未将其放在其它线程中执行。也就是说timer单线程执行的,那么问题来了,为何要这么费劲的封装一个Runnable接口又不进行多线程调用?

    AlarmManager

    AlarmManager是Android 系统封装的用于管理RTC的模块,RTC(Real Time Clock)是一个独立的硬件时钟,可以在 CPU休眠时正常运行,在预设的时间到达时,通过中断唤醒CPU。这意味着,如果我们用 AlarmManager来定时执行任务,CPU 可以正常的休眠,只有在需要运行任务时醒来一段很短的时间。

    参考:Timer与AlarmManager的区别

    Switch开关

    Switch mSwitch = (Switch)view.findViewById(R.id.setting_switch_notification_word); 
        mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener(){
    
            public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
                if (isChecked) {    
                    // switch on,开启通知栏单词
                    intentNotiService = new Intent(mContext, ServiceNotification.class);
                    pendingIntentNotiService = PendingIntent.getService(mContext, 0, intentNotiService, 0);
                    minute = 1;
                    startPendingIntent(pendingIntentNotiService);
                } else {
                    //switch off,关闭通知栏单词
                    stopPendingIntent(pendingIntentNotiService);
                }
            }
        });
    

    2015-8-21

    解决定时启动通知服务时pop up无效

    在Service的onStartCommand()里更新每次Notification需要更新的内容,如单词信息。无需改动的信息在onCreate()里初始化。

    • 原代码:

      @Override
      public int onStartCommand(Intent intent, int flags, int startId) {
          mBuilder
              .setContentText(WordsDB.wordClass.toString())   //测试用的单词信息
              .setWhen(System.currentTimeMillis())    //更新的时间
              .setTicker(WordsDB.wordClass.toString());   //在通知栏动画向上弹出
          startForeground(notifyID, mBuilder.build());    //display in "ongoing"
          Log.d("通知栏单词", WordsDB.wordClass.toString());
          return super.onStartCommand(intent, flags, startId);
      }
      

      按钮点击时会更新notification,也会在通知栏弹出提示。
      但使用AlarmManager定时启动该service时,会更新内容,但不会在通知栏弹出提示,只能自己打开通知栏才能查看到更新。

    • 改动:使用NotificationManager.notify()

      @Override
      public int onStartCommand(Intent intent, int flags, int startId) {
          mBuilder
              .setContentText(WordsDB.wordClass.toString())
              .setWhen(System.currentTimeMillis())
              .setTicker(WordsDB.wordClass.toString());   //popup in Status Bar
          mNotificationManager.notify(notifyID, notification);    //update data
          startForeground(notifyID, mBuilder.build());    //display in "ongoing"
          Log.d("通知栏单词", WordsDB.wordClass.toString());
          return super.onStartCommand(intent, flags, startId);
      }
      

    notify必须在startForeground前面,先更新通知的内容,再显示在前台。
    notifyID需一致。

    参考:How do I update the notification text for a foreground service in Android?

    2015-8-22

    输入自定义更新时间间隔

    用了3个EditText,分别输入小时、分钟、秒,默认30秒更新一次,即00:00:30
    获取焦点时自动清空EditText,失去焦点时若未输入任何内容,则回到默认值

    关于离开EditText时无法失焦(触摸EditText外的位置)

    在其parent view设置xml属性:

    android:clickable="true"
    android:focusable="true"
    android:focusableInTouchMode="true"
    

    隐藏软键盘keyboard

    1.在parent view设置

    android:clickable="true" 
    android:focusableInTouchMode="true" 
    

    2.hideKeyboard() method

    public void hideKeyboard(View view) {
        InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
        inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
    

    参考:how to hide soft keyboard on android after clicking outside EditText?

    2015-8-28

    改进:通过广播Broadcast启动服务service

    Broadcast静态registerReceiver与动态注册的区别

    1.动态注册的广播永远要快于静态注册的广播,不管静态注册的优先级设置的多高,不管动态注册的优先级有多低

    2.动态注册广播不是常驻型广播,也就是说广播跟随activity的生命周期。
    注意: 在activity结束前,移除广播接收器。

    静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。

    3.在同一个优先级下,谁先启动的快,谁将先接收到广播。

    动态注册代码:

    MyBroadcastReceiver  broadcast= new MyBroadcastReceiver();
    IntentFilter filter = new IntentFilter("action_name");
    registerReceiver(broadcast, filter);
    

    静态注册代码(在Manifest.xml中添加):

    <receiver android:name="com.example.MyReceiver" >
        <intent-filter>
            <action android:name="action_name" /> //可自定义action_name
        </intent-filter>
     </receiver>
    

    2015-8-29

    Slidingmenu的bug

    在设置如下时

    sm.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
    sm.setTouchModeBehind(SlidingMenu.TOUCHMODE_FULLSCREEN);
    

    1.菜单里的listview无法点击,解决:
    在Slidingmenu_lib里,

    • 修改CustomViewAbove.java,将onInterceptTouchEvent()onTouchEvent()case MotionEvent.ACTION_DOWN:break;改为return mQuickReturn;
      去掉initCustomViewAbove()里的setInternalPageChangeListener()
    • 修改CustomViewBehind.java,将onInterceptTouchEvent()onTouchEvent()return分别改为return mViewAbove.onInterceptTouchEvent(e);return mViewAbove.onTouchEvent(e);

    2.上述步骤后,若aboveview无法滑动,则在aboveview的root layout里添加android:clickable="true"

    参考:All touch events are consumed by CustomViewAbove #446

    2015-8-31

    悬浮窗单词

    参考教程:Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

    基本步骤

    1.在Manifest.xml声明权限

    <!-- 显示顶层浮窗 -->
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    

    注意:在MIUI上需要在设置中打开本应用的“显示悬浮窗”开关,并且重启应用,否则悬浮窗只能显示在本应用界面内,不能显示在手机桌面上。
    2.获取WindowManager

    // 获取应用的Context
    Context mContext = context.getApplicationContext();
    // 获取WindowManager
    WindowManager mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    

    3.获取layout

    LayoutInflater.from(mContext).inflate(R.layout.float_window, this);  
    View view = findViewById(R.id.window_layout);  
    

    4.参数设置

    smallWindowParams = new LayoutParams();  
    //LayoutParams.TYPE_PHONE: 全屏区域显示(不含状态栏)
    //LayoutParams.TYPE_SYSTEM_ALERT: 全屏区域显示(包含状态栏区域,被状态栏覆盖)
    //LayoutParams.TYPE_SYSTEM_ERROR: 全屏区域显示(包含状态栏区域,覆盖在状态栏上)
    smallWindowParams.type = LayoutParams.TYPE_SYSTEM_ERROR;  
    //设置图片格式:背景透明
    smallWindowParams.format = PixelFormat.RGBA_8888;  
    //FLAG_LAYOUT_IN_SCREEN:可在状态栏上显示
    smallWindowParams.flags =  LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_LAYOUT_IN_SCREEN;  
    smallWindowParams.gravity = Gravity.LEFT | Gravity.TOP;  
    smallWindowParams.width = ViewSmallFloatWindow.viewWidth;  
    smallWindowParams.height = ViewSmallFloatWindow.viewHeight;  
    smallWindowParams.x = 0;  
    smallWindowParams.y = Util.getStatusBarHeight(context);  
    

    让悬浮窗显示在状态栏上层

    LayoutParams.TYPE_SYSTEM_ERROR //在全屏区域显示(可在状态栏显示,覆盖状态栏)
    LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_LAYOUT_IN_SCREEN;
    

    注:

    • 这样设置下拉通知栏也会显示在悬浮窗下层。
    • FLAG_NOT_TOUCH_MODALFLAG_NOT_FOCUSABLE任选其一都可以,若只设置FLAG_LAYOUT_IN_SCREEN会导致悬浮窗的焦点变成全屏(悬浮窗外无法操作)。smallWindowParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_LAYOUT_IN_SCREEN | LayoutParams.FLAG_LAYOUT_INSET_DECOR | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;也可以。

    WindowManager

    • addView:添加一个悬浮窗,
    • updateViewLayout:更新悬浮窗的参数,
    • removeView:移除悬浮窗。

    WindowManager.LayoutParams这个类用于提供悬浮窗所需的参数,其中有几个经常会用到的变量:

    • type:确定悬浮窗的类型,一般设为2002,表示在所有应用程序之上,但在状态栏之下。

    • flags:确定悬浮窗的行为,比如说不可聚焦,非模态对话框等等,属性非常多,大家可以查看文档。

    • gravity:确定悬浮窗的对齐方式,一般设为左上角对齐,这样当拖动悬浮窗的时候方便计算坐标。

    • x:确定悬浮窗的位置,如果要横向移动悬浮窗,就需要改变这个值。

    • y:确定悬浮窗的位置,如果要纵向移动悬浮窗,就需要改变这个值。

    • width:指定悬浮窗的宽度。

    • height:指定悬浮窗的高度。

    获取屏幕宽度和高度

    Display中getHeight()和getWidth()被废弃

    Display dp=getWindowManager().getDefaultDisplay();
    int Height=dp.getHeight();  ---->The method getHeight() from the type Display is deprecated
    int Width=dp.getWidth();    ---->The method getWidth() from the type Display is deprecated
    

    替代的方法:

    //WindowManager wm = this.getWindowManager();
    WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    
    //DisplayMetrics dm = new DisplayMetrics();
    //wm.getDefaultDisplay().getMetrics(dm);
    DisplayMetrics dm = Resources.getSystem().getDisplayMetrics();
    
    SCREEN_WIDTH = dm.widthPixels;
    SCREEN_HEIGHT = dm.heightPixels;
    

    解析heightPixels和widthPixels:

    public int     heightPixels    The absolute height of the display in pixels.
    public int     widthPixels     The absolute width of the display in pixels.
    

    参考:
    1.Display中getHeight()和getWidth() 官方废弃
    2.How to get Screen metrics outside an Activity?

    点击手机“返回键”,移除悬浮窗

    在悬浮窗的view类里添加

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) { 
        switch (event.getKeyCode()) {
        case KeyEvent.KEYCODE_BACK:
            MyWindowManager.removeBigWindow(getContext());
        //  MyWindowManager.createSmallWindow(getContext());
            return true;
        default:
            return super.dispatchKeyEvent(event); 
        }
    }
    

    触摸悬浮窗外部区域,移除悬浮窗

    在悬浮窗的view类里添加

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        Rect rect = new Rect();
        //getGlobalVisibleRect方法的作用是获取视图在屏幕坐标中的可视区域;
        //getLocalVisibleRect的作用是获取视图本身可见的坐标区域,坐标以自己的左上角为原点(0,0)
        this.getGlobalVisibleRect(rect);
        if ( ! rect.contains(x, y) ) {
            MyWindowManager.removeBigWindow(getContext());
        //  MyWindowManager.createSmallWindow(getContext());
        }
        return super.dispatchTouchEvent(event);
    }
    

    参考:

    1. Android悬浮窗实现 使用WindowManager
    2. getGlobalVisibleRect和getLocalVisibleRect

    获取状态栏高度

    public static int getStatusBarHeight(Context context) {  
        if (statusBarHeight == 0) {  
            try {  
                Class<?> c = Class.forName("com.android.internal.R$dimen");  
                Object o = c.newInstance();  
                Field field = c.getField("status_bar_height");  
                int x = (Integer) field.get(o);  
                statusBarHeight = context.getResources().getDimensionPixelSize(x);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
        return statusBarHeight;  
    }
    

    参考:Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

    TextView横向自动滚动实现跑马灯效果

    1.原生android自带的跑马灯效果,直接配置TextView属性

    android:singleLine="true"  
    android:ellipsize="marquee" 
    android:focusable="true"  
    android:focusableInTouchMode="true" 
    android:marqueeRepeatLimit="marquee_forever"  
    
    • android:singleLine=true 表示使用单行文字,多行文字也就无所谓使用Marquee效果了。
    • android:marqueeRepeatLimit,设置走马灯滚动的次数。marquee_forever为无限循环。
    • android:ellipsize,设置了文字过长时如何切断文字,可以有none, start,middle, end, 如果使用走马灯效果则设为marquee.
    • android:focusable,Android的缺省行为是在控件获得Focus时才会显示走马灯效果

    判断当前界面是否是桌面

    private boolean isHome() {  
        ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);  
        List<RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);  
        return getHomes().contains(rti.get(0).topActivity.getPackageName());  
    } 
    

    获得属于桌面的应用的应用包名称

    private List<String> getHomes() {  
        List<String> names = new ArrayList<String>();  
        PackageManager packageManager = this.getPackageManager();  
        Intent intent = new Intent(Intent.ACTION_MAIN);  
        intent.addCategory(Intent.CATEGORY_HOME);  
        List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,  
                PackageManager.MATCH_DEFAULT_ONLY);  
        for (ResolveInfo ri : resolveInfo) {  
            names.add(ri.activityInfo.packageName);  
        }  
        return names;  //返回包含所有包名的字符串列表
    }  
    

    2015-9-3

    PendingIntent

    flag

    • 0:默认为无论是否存在相同的 PendingIntent 对象都会创建一个新的 PendingIntent。

    • FLAG_CANCEL_CURRENT: 如果当前系统中已经存在一个相同的 PendingIntent 对象,那么就将先将已有的 PendingIntent 取消,然后重新生成一个 PendingIntent 对象。

    • FLAG_NO_CREATE: 如果当前系统中不存在相同的 PendingIntent 对象,系统将不会创建该 PendingIntent 对象而是直接返回 null。

    • FLAG_ONE_SHOT: 该 PendingIntent 只作用一次,如果该 PendingIntent 对象已经触发过一次,那么下次再获取该 PendingIntent 并且再触发时,系统将会返回一个 SendIntentException,在使用这个标志的时候一定要注意哦。

    • FLAG_UPDATE_CURRENT: 如果系统中已存在该 PendingIntent 对象,那么系统将保留该 PendingIntent 对象,但是会使用新的 Intent 来更新之前 PendingIntent 中的 Intent 对象数据,例如更新 Intent 中的 Extras。这个非常有用,例如之前提到的,我们需要在每次更新之后更新 Intent 中的 Extras 数据,达到在不同时机传递给 MainActivity 不同的参数,实现不同的效果。

    参考:
    1.What happens if you set the flag on a PendingIntent to 0?
    2.Android PendingIntent 的一些小迷惑

    2015-9-5

    SeekBar拖动条

    SeekBar总是自动获取焦点(点击/触摸SeekBar外部区域时,thumb会有响应(变色))

    .xml中给添加属性:

    android:clickable="true"
    android:focusable="true"
    android:focusableInTouchMode="true"
    

    TextView显示音标

    1.下载音标字体,如TOPhonetic.ttf,将音标字体文件放在assets/font目录下
    2.为TextView设置属性

    Typeface mFace = Typeface.createFromAsset(getAssets(), "font/TOPhonetic.ttf"); 
    wordPhoneticTextView.setTypeface(mFace);
    

    若显示getAssets() is undefined,则改为context.getAssets()
    参考:Android如何显示音标

    2015-9-6

    TextView设置不同的字体风格

    悬浮窗里只用了一个TextView,便于显示跑马灯效果。为了显示音标使用了TOPhonetic字体,但这样显示的英文和中文都不太喜欢,想只有音标使用TOPhonetic字体,单词和释义跟随手机当前字体。

    1.使用Html.fromHtml

    如:

    mTextView.setTextView(Html.fromHtml("<font color='red'><b>" + "红色字体"
                + "</b></font>TextView学习显示不同颜色"));
    

    Textview并不支持所有的html标签。如果更复杂的,可以直接使用webview组件。
    查找资料有人说<font face="verdana" color="green">This is some text!</font>,想要更改字体必须手机上安装了此字体。(未测试是否可行,但直接使用的确无效)

    String Resources支持的tag

    - <a> (supports attributes "href")
    - <annotation>
    - <b>
    - <big>
    - <font> (supports attributes "height", "size", "fgcolor" and "bicolor", as integers)
    - <i>
    - <li>
    - <marquee>
    - <small>
    - <strike>
    - <sub>
    - <sup>
    - <tt>
    - <u>
    

    Html.fromHtml()支持的tag

    - <a> (supports attribute "href")
    - <b>
    - <big>
    - <blockquote>
    - <br>
    - <cite>
    - <dfn>
    - <div>
    - <em>
    - <font> (supports attributes "color" and "face")
    - <i>
    - <img> (supports attribute "src". Note: you have to include an ImageGetter to handle retrieving a Drawable for this tag)
    - <p>
    - <small>
    - <strong>
    - <sub>
    - <sup>
    - <tt>
    - <u>
    

    支持的颜色:

    - aqua
    - black
    - blue
    - fuchsia
    - green
    - grey
    - lime
    - maroon
    - navy
    - olive
    - purple
    - red
    - silver
    - teal
    - white
    - yellow
    

    参考:
    1.HTML in TextViews
    2.Android 字体设置注意的地方

    2.使用Spannable

    1.CustomTypefaceSpan Class:

    import android.graphics.Paint;
    import android.graphics.Typeface;
    import android.text.TextPaint;
    import android.text.style.TypefaceSpan;
    
    public class CustomTypefaceSpan extends TypefaceSpan {
        private final Typeface newType;
    
        public CustomTypefaceSpan(String family, Typeface type) {
            super(family);
            newType = type;
        }
    
        @Override
        public void updateDrawState(TextPaint tp) {
            applyCustomTypeFace(tp, newType);
        }
    
        @Override
        public void updateMeasureState(TextPaint paint) {
            applyCustomTypeFace(paint, newType);
        }
    
        private static void applyCustomTypeFace(Paint paint, Typeface tf) {
            int oldStyle;
            Typeface old = paint.getTypeface();
            if (old == null) {
                oldStyle = 0;
            } else {
                oldStyle = old.getStyle();
            }
    
            int fake = oldStyle & ~tf.getStyle();
            if ((fake & Typeface.BOLD) != 0) {
                paint.setFakeBoldText(true);
            }
    
            if ((fake & Typeface.ITALIC) != 0) {
                paint.setTextSkewX(-0.25f);
            }
    
            paint.setTypeface(tf);
        }
    }
    

    2.使用方法

    Typeface font1 = Typeface.createFromAsset(getAssets(), "font/font1.ttf");
    Typeface font2 = Typeface.createFromAsset(getAssets(), "font/font2.ttf");   
    String str = "abcdefghijk";
    int len = str.length();
    SpannableStringBuilder ss = new SpannableStringBuilder(str);
    ss.setSpan (new CustomTypefaceSpan("", font1), 0, 2,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
    ss.setSpan (new CustomTypefaceSpan("", font2), 2, 5,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
    ss.setSpan (new CustomTypefaceSpan("", Typeface.DEFAULT), 5, len,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
    textview.setText(ss);
    

    注意:
    setSpan(Object what, int start, int end, int flags)的参数:
    字符的位置从0开始计数

    • int start:开始位置(以0为基数)
    • int end:从start开始一共(end-start)个字符,即实际结束位置为(end-1)
    • int flags:有2个值SPAN_EXCLUSIVE_INCLUSIVESPAN_EXCLUSIVE_EXCLUSIVE,使用中没发现有什么区别。O.O

    参考:
    1.android中用Spannable在TextView中设置超链接、颜色、字体
    2.How set Spannable object font with custom font

    “通知栏单词”无法正确显示音标

    可以通过tickerText.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, tickerText.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);设置加粗或斜体,但用自定义CustomTypefaceSpan时无效。未找到解决办法。
    最后发现,其实手机是可以直接显示音标的。。(直接复制音标比如setTicker("[əˈsɪst]"),手机上可以正常显示!!!)
    看来是源数据的编码问题@_@
    又找了几份单词excel文件,终于找到个可以正常显示的了。下载单词音标释义版excel
    换了数据后,一切正常显示。。也不用替换字体了~

    SQLite数据库

    记得之前有做笔记的,但是竟然找不到了。。
    以前在实习公司用过Navicat,但是启动特别缓慢(现在看来是电脑问题—_—),加上网上也有人说Navicat臃肿然后推荐了一堆别的软件,今天之前都用的SQLiteStudio,实话说,不太好用。刚刚新的excel文件虽然在excel里音标是正常的,但导出到txt音标又乱码了,更换字体无效!!!于是只好安装Navicat,可以直接导入excel数据,发现在我的电脑并不卡—_—果断弃用SQLiteStudio。所以这么曲折都是为什么。。。

    2015-9-7

    新增时间字段,类型选择TEXT,在下方默认栏里写(datetime('now','localtime')) (注意最外要有括号),即可自动添加当前时区的时间。若默认CURRENT_TIMESTAMP,则时区为GMT。
    参考:
    1.sqlite database default time value ‘now’
    2.Sqlite: CURRENT_TIMESTAMP is in GMT, not the timezone of the machine
    3.How to enter function values in tables ?

    Button的点击和父控件的冲突

    1.在“悬浮窗单词”SmallFloatWindowView中,一个Button用来播放单词读音,一个TextView显示单词。
    最初是对“悬浮窗单词”的SmallFloatWindowView进行onTouchEvent监听,对Button进行OnClickListener监听,但这样Button无法移动,Textview可以移动。
    改为:button.setOnTouchListener监听onTouch行为,在onTouch里执行button的操作。
    2.虽然解决了问题,但2个监听有很多重复代码,只是处理点击有所不同。
    将两者都设置setOnTouchListener,复写onTouch

    @Override
    public boolean onTouch(View v, MotionEvent ev) {
        switch (ev.getAction()) {  
        case MotionEvent.ACTION_DOWN:  
            xInView = ev.getX();  
            yInView = ev.getY();  
            xDownInScreen = ev.getRawX();  
            yDownInScreen = ev.getRawY();  
            xInScreen = ev.getRawX();  
            yInScreen = ev.getRawY();  
            break;  
        case MotionEvent.ACTION_MOVE:  
            xInScreen = ev.getRawX();  
            yInScreen = ev.getRawY();  
            updateViewPosition();  
            break;  
        case MotionEvent.ACTION_UP:  
            if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) {
                switch (v.getId()) {
                case R.id.small_float_window_layout:
                    Log.d(VIEW_LOG_TAG, "点击了float window");
                    break;
                case R.id.small_float_window_play_btn:
                    Log.d(VIEW_LOG_TAG, "点击了btn");
                    break;
                default:
                    break;
                }
            }  
            break;  
        default:  
            break;  
        }
        return true;
    }  
    

    注意:最后要返回true,否则只能监听到btn的行为。
    参考:MotionEvent.ACTION_UP on Textview

    2015-9-8

    RecyclerView添加PopMenu菜单

    在ViewHolder里配置:
    1.setOnClickListener

    public ViewHolder( View v ) {  
            super(v);  
            this.view = v;
            v.setOnLongClickListener(this);
    } 
    

    2.在OnLongClickListener创建PopupMenu

    @Override
    public boolean onLongClick(View v) {
        if ( v == this.view ) {
            PopupMenu popMenu = new PopupMenu(v.getContext(), v);
            popMenu.inflate(R.menu.wordbook_context_menu);
            popMenu.setOnMenuItemClickListener(this);
            popMenu.show();
        }
        return false;
    }
    

    3.在OnMenuItemClickListener给menu items添加操作

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.wordbook_context_menu_edit:
            Toast.makeText(view.getContext(),tvWord.getText().toString()+item.getTitle(), Toast.LENGTH_SHORT).show();
            break;
        case R.id.wordbook_context_menu_delete:
            Toast.makeText(view.getContext(),tvWord.getText().toString()+item.getTitle(), Toast.LENGTH_SHORT).show();
            break;
        case R.id.wordbook_context_menu_addto:
            Toast.makeText(view.getContext(),tvWord.getText().toString()+item.getTitle(), Toast.LENGTH_SHORT).show();
            break;
        default:
            break;
        }
        return true;
    }
    

    参考:Adding context menus to RecyclerView items

    2015-9-9

    SQLite数据库

    获取所有表名

    db = wordsDbHelper.getReadableDatabase();
    List<String> tableList = new ArrayList<String>();
    Cursor cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table' AND name!='android_metadata' order by name", null);
    while(cursor.moveToNext()){
        tableList.add(cursor.getString(0));
    }
    

    参考:How to select all tables names instead android_metadata android SQLite

    表重命名

    public static void alterTableName(String oldName, String newName) {
        if ( ! getTableList().contains(newName) ) {
            db = wordsDbHelper.getReadableDatabase();
            db.execSQL("ALTER TABLE " + oldName + " RENAME TO " + newName + ";");
            db.close();
        }
    }
    

    RecyclerView详细使用

    参考:RecyclerView使用介绍

    RecyclerView Adapter更新数据集

    public void updateList(List<WordCls> newList) {
        this.wordsList = newList;
        notifyDataSetChanged();
    }
    

    AlertDialog对话框

    参考:Android的AlertDialog详解

    2015-9-10

    RecyclerView添加点击事件,并获取item position

    参考:
    1.RecyclerView使用介绍
    2.Easy Implementation of RecyclerView custom onItemClickListener

    RecyclerView水平滑动类似viewpager——继承SnappingRecyclerView

    参考:
    1.SnappingRecyclerView.java
    2.Snappy scrolling in RecyclerView

    RecyclerView.Adapter根据viewType动态加载不同的item布局

    在Adapter里
    1.设置viewType

    public static final int TYPE_VIEW_VERTICAL = 0;
    public static final int TYPE_VIEW_HORIZON = 1;
    
    @Override
    public int getItemViewType(int position) {
        //也可以根据item的position设置不同的viewType
        return viewType;
    }
    
    public void setItemViewType(int viewType) {
        this.viewType = viewType;
    }
    

    2.一个item布局对应一个ViewHolder
    自定义一个BaseViewHolder,让所有的ViewHolder继承它。

    public class BaseViewHolder extends RecyclerView.ViewHolder {
        public TextView tvWord;  
        public TextView tvPhonetic;  
        public TextView tvDefinition;  
    
        public BaseViewHolder(View v) {
            super(v);
        }
    }
    
    public class VerticalViewHolder extends BaseViewHolder {
        public ImageButton imgBtn;
    
        public VerticalViewHolder( View v) {  
            super(v); 
    
            tvWord = (TextView) v.findViewById(R.id.wordcard_vertical_tv_word);  
            tvPhonetic = (TextView) v.findViewById(R.id.wordcard_vertical_tv_phonetic);  
            tvDefinition = (TextView) v.findViewById(R.id.wordcard_vertical_tv_definition);  
            imgBtn = (ImageButton) v.findViewById(R.id.wordcard_vertical_imgbtn);  
        }
    }  
    
    public class HorizonViewHolder extends BaseViewHolder {
    
        public HorizonViewHolder( View v) {  
            super(v); 
    
            tvWord = (TextView) v.findViewById(R.id.wordcard_horizon_tv_word);  
            tvPhonetic = (TextView) v.findViewById(R.id.wordcard_horizon_tv_phonetic);  
            tvDefinition = (TextView) v.findViewById(R.id.wordcard_horizon_tv_definition);  
        }
    }
    

    3.根据viewType执行不同的操作

    public class WordRecyclerViewAdapter extends RecyclerView.Adapter<WordRecyclerViewAdapter.**BaseViewHolder**> {
        ……
    
        @Override  
        public **BaseViewHolder** onCreateViewHolder( ViewGroup parent, int **viewType** )  
        {  
            final **BaseViewHolder** viewHolder;
            View v ;
    
            switch (**viewType**) {
            case TYPE_VIEW_HORIZON:
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout.wordbook_item_horizontal_cardview, parent, false);  
                viewHolder = new HorizonViewHolder(v);
                break;
            case TYPE_VIEW_VERTICAL:
            default:
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout.wordbook_item_vertical_cardview, parent, false);  
                viewHolder = new VerticalViewHolder(v);
                break;
            }
    
            return viewHolder;   
        }
    
        @Override  
        public void onBindViewHolder( **BaseViewHolder** baseViewHolder, int position ) {  
            WordCls wordCls = wordsList.get(position);  
    
            baseViewHolder.itemView.setTag(wordCls);
    
            baseViewHolder.tvWord.setText(wordCls.getWord());  
            baseViewHolder.tvPhonetic.setText(wordCls.getPhonetic());  
            baseViewHolder.tvDefinition.setText(wordCls.getDefinition());  
    
            switch (**baseViewHolder.getItemViewType()**) {
            case TYPE_VIEW_HORIZON:
                HorizonViewHolder horizonViewHolder = (HorizonViewHolder) baseViewHolder;
                ……
                break;
            case TYPE_VIEW_VERTICAL:
            default:
                VerticalViewHolder verticalViewHolder = (VerticalViewHolder) baseViewHolder;
                verticalViewHolder.imgBtn.setOnClickListener(new OnClickListener() {
    
                    @Override
                    public void onClick(View v) {
                    }
                });
                ……
                break;
            }
        }  
    }
    

    参考:
    1.How to Change the viewType of a RecyclerView item onClick
    2.Recyclerview and handling different type of row inflation

    2015-9-11

    保存和恢复RecyclerView(Scroll)的精确滑动位置

    1.scrollby

    private int mScrollY;
    private int mScrollYState;
    //保存
    private RecyclerView.OnScrollListener mTotalScrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            mScrollY += dy;
        }
    };
    //恢复
    mScrollYState = mScrollY;
    mRecyclerView.scrollBy(0, mScrollYState);
    

    参考:Refreshing data in RecyclerView and keeping its scroll position

    2.onSaveInstanceState

    Parcelable recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();//保存
    recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);//恢复
    

    直接存储RecyclerView的InstanceState,但只能在fragment的生命周期里使用
    参考:

    2015-9-15
    3.#保存和恢复RecyclerView(Scroll)的精确滑动位置——改进版
    尝试了好几天,终于想到了一种比较好的方式,能保存精确位置至本地,随意切换列表数据也能恢复相应表的浏览位置,不过还是有一点点缺陷,第一页的item会跳动一下。

    • scrollToPosition是根据你的操作方向来判断目标item是显示在顶端还是显示在底端的,手指上滑则显示在底端,手指下滑则显示在顶端。那么在切换列表时,则是根据现在的currentPositionscrollToPosition(position)position来判断滑动方向的。这样需要记录顶端和底端的2个偏移量。

    • 切换列表时,是先计算好要滑动的位置,才会显示视图的,所以只能根据当前列表(切换前显示的列表)的视图来计算位置,切换后才会滑到正确的位置。即scrollToPosition会滑到顶端还是底端要靠你自己判断的。

    • RecyclerView的界面还未出现时,比如第一次打开fragment或者切换到新的数据列表时,findFirstVisibleItemPosition的值是-1,这时是无法获得child View的。调试时发现,只有当手机上能看到RecyclerView时,才能获取child view。并且getChildAt(int index)中的index是指child在当前RecyclerView视图中显示的item个数中的index,而不是针对整个dataset的,故只能获取到正在显示的某一个child的View。

    • 若切换时currentFirstVisiblePosition < savedFirstVisiblePosition,即RecyclerView需要将列表往上拉(手指上滑)以显示下面position较大的部分,scrollToPosition会显示在底端。
      但是当savedFirstVisiblePosition在其dataset中的位置在手机中显示是在第一页时,scrollToPosition是无法滑动到底端的。我想到的是scrollToPosition后,让列表scrollBy(0, recyclerView.getHeight());往上滑一部分,再scrollToPosition();回来,这样目标item就会显示在顶端了,再用smoothScrollBy(0, dyTop);就可以了。这里最后一步没有用scrollBy是因为没有效,我也不知道为什么,可能不支持连续滑动多次吧,也可以将scrollBy放在handler里,效果和smoothScrollBy差不多;但是这2种方法都会出现跳动的动画。

    private void saveRecyclerViewPosition(String tableName) {
        firstVisibleItemPosition = ((LinearLayoutManager)recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
    
        savedFirstVisibleChild = recyclerView.getChildAt(0);
        //dy正,手指将列表往上拉
        //dy负,手指将列表往下拉
        dyTop = savedFirstVisibleChild.getHeight() - savedFirstVisibleChild.getBottom(); 
        dyBottom = recyclerView.getHeight() - savedFirstVisibleChild.getBottom();
    
        prefEditorSettings.putInt(KEY_RECYCLERVIEW_SCROLL_POSITION + tableName, firstVisibleItemPosition);
        prefEditorSettings.putInt(KEY_RECYCLERVIEW_SCROLL_DY_TOP + tableName, dyTop);
        prefEditorSettings.putInt(KEY_RECYCLERVIEW_SCROLL_DY_BOTTOM + tableName, dyBottom);
        prefEditorSettings.commit(); 
    }
    
    private void restoreRecyclerViewPosition(String tableName) {
        if ( recyclerView != null) {
            savedFirstVisiblePosition = prefSettings.getInt(KEY_RECYCLERVIEW_SCROLL_POSITION + tableName, 0);
            dyTop = prefSettings.getInt(KEY_RECYCLERVIEW_SCROLL_DY_TOP + tableName, 0);
            dyBottom = prefSettings.getInt(KEY_RECYCLERVIEW_SCROLL_DY_BOTTOM + tableName, 0);
    
            currentFirstVisiblePosition =  ((LinearLayoutManager)recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
    
            recyclerView.scrollToPosition(savedFirstVisiblePosition);
    
            if(currentFirstVisiblePosition > -1) {  
                if (currentFirstVisiblePosition >= savedFirstVisiblePosition) { //savedFirstVisiblePosition在顶部
                    recyclerView.scrollBy(0, dyTop);
                } else if (currentFirstVisiblePosition < savedFirstVisiblePosition){    //savedFirstVisiblePosition在底部
                    if (savedFirstVisiblePosition > 4)
                        recyclerView.scrollBy(0, dyBottom);             
                    else {  //第一页的item用handler/smoothScrollBy会有跳转动作显示,暂时会找到合适的办法
                        recyclerView.scrollBy(0, recyclerView.getHeight());
                        recyclerView.scrollToPosition(savedFirstVisiblePosition);
                        recyclerView.smoothScrollBy(0, dyTop);
                    }
                } 
            } else {    //第一次打开,还未出现界面
                recyclerView.scrollToPosition(savedFirstVisiblePosition);
                recyclerView.scrollBy(0, dyTop);
            }
        }
    }
    
    • 这个方法不受生命周期的限制,但在上述情况中视觉体验不太好,所以可以跟方法2结合起来使用。

      private void saveRecyclerViewPosition(String tableName) {
          recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();
          hmRecyclerViewState.put(tableName, recyclerViewState);
      
          firstVisibleItemPosition = ((LinearLayoutManager)recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
      
          savedFirstVisibleChild = recyclerView.getChildAt(0);
          //dy正,手指将列表往上拉
          //dy负,手指将列表往下拉
          dyTop = savedFirstVisibleChild.getHeight() - savedFirstVisibleChild.getBottom(); 
          dyBottom = recyclerView.getHeight() - savedFirstVisibleChild.getBottom();
      
          prefEditorSettings.putInt(KEY_RECYCLERVIEW_SCROLL_POSITION + tableName, firstVisibleItemPosition);
          prefEditorSettings.putInt(KEY_RECYCLERVIEW_SCROLL_DY_TOP + tableName, dyTop);
          prefEditorSettings.putInt(KEY_RECYCLERVIEW_SCROLL_DY_BOTTOM + tableName, dyBottom);
          prefEditorSettings.commit();
      }
      
      private void restoreRecyclerViewPosition(String tableName) {
          recyclerViewState = hmRecyclerViewState.get(tableName);
          if (recyclerViewState != null) {
              recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
          } else {
              if ( recyclerView != null) {
                  savedFirstVisiblePosition = prefSettings.getInt(KEY_RECYCLERVIEW_SCROLL_POSITION + tableName, 0);
                  dyTop = prefSettings.getInt(KEY_RECYCLERVIEW_SCROLL_DY_TOP + tableName, 0);
                  dyBottom = prefSettings.getInt(KEY_RECYCLERVIEW_SCROLL_DY_BOTTOM + tableName, 0);
      
                  currentFirstVisiblePosition =  ((LinearLayoutManager)recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
      
                  recyclerView.scrollToPosition(savedFirstVisiblePosition);
      
                  if(currentFirstVisiblePosition > -1) {
                      if (currentFirstVisiblePosition >= savedFirstVisiblePosition) { //savedFirstVisiblePosition在顶部
                          recyclerView.scrollBy(0, dyTop);
                      } else if (currentFirstVisiblePosition < savedFirstVisiblePosition){    //savedFirstVisiblePosition在底部
                          if (savedFirstVisiblePosition > 4)
                              recyclerView.scrollBy(0, dyBottom);             
                          else {  //第一页的item用handler/smoothScrollBy会有跳转动作显示,暂时会找到合适的办法
                              recyclerView.scrollBy(0, recyclerView.getHeight());
                              recyclerView.scrollToPosition(savedFirstVisiblePosition);
                              recyclerView.smoothScrollBy(0, dyTop);
                          }
                      } 
                  } else {    //第一次打开,还未出现界面
                      recyclerView.scrollToPosition(savedFirstVisiblePosition);
                      recyclerView.scrollBy(0, dyTop);
                  }
              }
          }
      }
      

    slidingmenu切换fragment时的优化

    避免每次切换fragment时都重新加载view。
    注意:一个activity只有一个actionbar。

    1.SlidingmenuFragment

    @Override
    public void onListItemClick(ListView lv, View v, int position, long id) {
        newContentFragment = null;
    
        object = lv.getItemAtPosition(position);
        str=(String)object;
    
        getActivity().setTitle(str);
        actionBar.setDisplayShowCustomEnabled(false);
    
        if (str.matches(getResources().getString(R.string.center))){
            if (centerFragment == null)
                centerFragment = new CenterFragment();
            newContentFragment = centerFragment;
        } else if (str.matches(getResources().getString(R.string.home))){
            if (homeFragment == null)
                homeFragment = new HomeFragment(getActivity());
            newContentFragment = homeFragment;
        } else if (str.matches(getResources().getString(R.string.wordbook))){
            if (wordBookFragment == null)
                wordBookFragment = new WordBookFragment();
            newContentFragment = wordBookFragment;
            //加载自定义actionbar布局,在wordBookFragment里实现actionBar.setCustomView()后,在这里设置为true即可。
            actionBar.setDisplayShowCustomEnabled(true);
        } else if (str.matches(getResources().getString(R.string.BBS))){
            if (bbsFragment == null)
                bbsFragment = new BBSFragment();
            newContentFragment = bbsFragment;
        } else if (str.matches(getResources().getString(R.string.settings))){
            if (settingsFragment == null)
                settingsFragment = new SettingsFragment();
            newContentFragment = settingsFragment;
        }
    
    
        if (newContentFragment != null){
            switchFragment(newContentFragment);
        }
    }
    
    private void switchFragment(Fragment fragment) {
        if (getActivity() == null)
            return;
    
        if (getActivity() instanceof MainActivity) {
            MainActivity main = (MainActivity) getActivity();
            main.switchContent(fragment);
        } 
    }
    

    2.MainActivity

    public void switchContent(Fragment newFragment) {
        if ( contentFragment == null || newFragment == null)
            return;
    
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
    
        if (contentFragment != newFragment) {
            if (!newFragment.isAdded()) {
                // 隐藏当前的fragment,add下一个到Activity中
                transaction.hide(contentFragment).add(R.id.content_frame,newFragment).commit();
            } else {
               // 隐藏当前的fragment,显示下一个
                transaction.hide(contentFragment).show(newFragment).commit();
            }
            contentFragment = newFragment;
        }
    
        //通过handler来避免滑动卡顿的情况
        handler.post(new Runnable() {
    
            @Override
            public void run() {
                sm.showContent();
            }
        });
    }
    

    参考:

    2015-9-16

    联网解析JSON数据

    本来想用金山的,但一直没收到key,发现扇贝的查单词不用key,就用了扇贝的API
    1.在AsyncTask里联网并解析数据

    class ParseJsonTask extends AsyncTask<String, Void, Boolean> {
        HorizonViewHolder horizonViewHolder;
        WordCls wordCls;
        int position;
    
        String definitionEN;
        String definitionCN;
        String audioUrlUS;
    
        Handler handler;    //用来传值
    
        public ParseJsonTask(HorizonViewHolder horizonViewHolder,
                WordCls wordCls, int position, Handler handler) {
            super();
            this.horizonViewHolder = horizonViewHolder;
            this.wordCls = wordCls;
            this.position = position;
            this.handler = handler;
        }
    
        @Override
        protected void onPreExecute() {
            horizonViewHolder.progressBar.setVisibility(View.VISIBLE);
            super.onPreExecute();
        }
    
        @Override
        protected void onPostExecute(Boolean result) {
            Message msg = handler.obtainMessage();
    
            horizonViewHolder.progressBar.setVisibility(View.INVISIBLE);
            if (result) {
                wordCls.setDefinitionEN(definitionEN);
                wordCls.setDefinitionCN(definitionCN);
                wordCls.setAudioUrlUS(audioUrlUS);
                wordCls.setLoaded(true);
                WordsManager.addWordLoadInfo(tableName, wordCls);
                updateItem(position, wordCls);
    
                msg.what = position;
            }else {
                msg.what = 0;
                Log.i(wordCls.getWord(), "获取数据失败");
            }
    
            handler.sendMessage(msg);
    
            super.onPostExecute(result);
        }
    
        @Override
        protected Boolean doInBackground(String... params) {
            String path = "https://api.shanbay.com/bdc/search/?word=" + wordCls.getWord();
            try {
                URL url = new URL(path);
                Source source = new Source(url.openConnection());   //jericho-html-3.1.jar
                String jsonstr = source.toString();
    
                JSONObject jsonObj = new JSONObject(jsonstr);
    
                JSONObject data = jsonObj.getJSONObject("data");
    
                JSONObject defEN = data.getJSONObject("en_definition");
                definitionEN = defEN.getString("pos") + "." + defEN.getString("defn"); 
    
                JSONObject defCN = data.getJSONObject("cn_definition");
                definitionCN = defCN.getString("pos") + defCN.getString("defn"); 
    
                audioUrlUS = data.getString("us_audio");
    
                return true;
            } catch (Exception e) {
                Toast.makeText(mContext, "获取数据失败", Toast.LENGTH_SHORT).show();
                return false;
            }
        }
    }
    
    if ( ! wordCls.isLoaded() ) {
        Handler handler = new Handler(){
    
            @Override
            public void handleMessage(Message msg) {    根据AsyncTask的执行结果传递的值来判断下一步操作
                if (msg.what == position)
                    horizonViewHolder.tvHint.setVisibility(View.INVISIBLE);
                else
                    horizonViewHolder.tvHint.setText("数据获取失败,请重试");
            }
    
        };
        ParseJsonTask parseJsonTask = new ParseJsonTask(horizonViewHolder, wordCls, position, handler);
        parseJsonTask.execute();
    } 
    

    参考:

    2015-9-17

    RecyclerView水平滑动类似viewpager——自定义方法实现

    之前继承的SnappingRecyclerView,水平状态的时候,点击item里的TextView更新会出现错位,有时还会跑到第一个位置去,并且感觉他的有点复杂。。
    然后发现自己写一个实现也并不难@_@ 我的每一个item都是全屏的卡片,所以这样就可以了。

    private void scrollToCenter(RecyclerView recyclerView) {
        firstVisibleItemPosition = ((LinearLayoutManager)recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
        lastVisibleItemPosition = ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastVisibleItemPosition();
    
        if ( firstVisibleItemPosition < lastVisibleItemPosition ) {
            recyclerViewWidth = recyclerView.getWidth();
            firstVisibleChild = recyclerView.getChildAt(0);
            firstChildVisibleWidth = firstVisibleChild.getRight();
            if ( firstChildVisibleWidth > ( recyclerViewWidth / 2 ) )
                recyclerView.smoothScrollToPosition(firstVisibleItemPosition);
            else if ( firstChildVisibleWidth < ( recyclerViewWidth / 2 ) )
                recyclerView.smoothScrollToPosition(lastVisibleItemPosition);
        }
    }
    
    @Override
    public void onScrollStateChanged(final RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
    
        switch (newState) {
        case RecyclerView.SCROLL_STATE_IDLE:
            switch (wordCardAdapter.getItemViewType()) {
            case TYPE_VIEW_HORIZON:
                scrollToCenter(recyclerView);
                break;
            case TYPE_VIEW_VERTICAL:
            default:
                break;
            }
            break;
        case RecyclerView.SCROLL_STATE_DRAGGING:
            break;
        case RecyclerView.SCROLL_STATE_SETTLING:
            break;
     }
    }
    

    actionbar下拉列表——PopupWindow

    View view = LayoutInflater.from(context).inflate(R.layout.popmenu, null);       
    popupWindow = new PopupWindow(view, 100, LayoutParams.WRAP_CONTENT);
    // 使其聚集
    popupWindow.setFocusable(true);
    // 设置允许在外点击消失
    popupWindow.setOutsideTouchable(true);
    //刷新状态(必须刷新否则无效)
    popupWindow.update();
    // 这个是为了点击“返回Back”也能使其消失,并且并不会影响你的背景
    popupWindow.setBackgroundDrawable(new BitmapDrawable()); 
    
    popupWindow.showAsDropDown(v); //设置显示位置
    

    参考:

    2015-9-18

    RecyclerView高度wrap_content无效

    自定义MyLayoutManager

    /*
     * Copyright 2015 serso aka se.solovyev
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     *
     * Contact details
     *
     * Email: se.solovyev@gmail.com
     * Site:  http://se.solovyev.org
     */
    
    package org.solovyev.android.views.llm;
    
    import android.content.Context;
    import android.graphics.Rect;
    import android.support.v4.view.ViewCompat;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.view.View;
    
    import java.lang.reflect.Field;
    
    /**
     * {@link android.support.v7.widget.LinearLayoutManager} which wraps its content. Note that this class will always
     * wrap the content regardless of {@link android.support.v7.widget.RecyclerView} layout parameters.
     * <p/>
     * Now it's impossible to run add/remove animations with child views which have arbitrary dimensions (height for
     * VERTICAL orientation and width for HORIZONTAL). However if child views have fixed dimensions
     * {@link #setChildSize(int)} method might be used to let the layout manager know how big they are going to be.
     * If animations are not used at all then a normal measuring procedure will run and child views will be measured during
     * the measure pass.
     */
    public class LinearLayoutManager extends android.support.v7.widget.LinearLayoutManager {
    
        private static boolean canMakeInsetsDirty = true;
        private static Field insetsDirtyField = null;
    
        private static final int CHILD_WIDTH = 0;
        private static final int CHILD_HEIGHT = 1;
        private static final int DEFAULT_CHILD_SIZE = 100;
    
        private final int[] childDimensions = new int[2];
        private final RecyclerView view;
    
        private int childSize = DEFAULT_CHILD_SIZE;
        private boolean hasChildSize;
        private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS;
        private final Rect tmpRect = new Rect();
    
        @SuppressWarnings("UnusedDeclaration")
        public LinearLayoutManager(Context context) {
            super(context);
            this.view = null;
        }
    
        @SuppressWarnings("UnusedDeclaration")
        public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
            super(context, orientation, reverseLayout);
            this.view = null;
        }
    
        @SuppressWarnings("UnusedDeclaration")
        public LinearLayoutManager(RecyclerView view) {
            super(view.getContext());
            this.view = view;
            this.overScrollMode = ViewCompat.getOverScrollMode(view);
        }
    
        @SuppressWarnings("UnusedDeclaration")
        public LinearLayoutManager(RecyclerView view, int orientation, boolean reverseLayout) {
            super(view.getContext(), orientation, reverseLayout);
            this.view = view;
            this.overScrollMode = ViewCompat.getOverScrollMode(view);
        }
    
        public void setOverScrollMode(int overScrollMode) {
            if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER)
                throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode);
            if (this.view == null) throw new IllegalStateException("view == null");
            this.overScrollMode = overScrollMode;
            ViewCompat.setOverScrollMode(view, overScrollMode);
        }
    
        public static int makeUnspecifiedSpec() {
            return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        }
    
        @Override
        public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
            final int widthMode = View.MeasureSpec.getMode(widthSpec);
            final int heightMode = View.MeasureSpec.getMode(heightSpec);
    
            final int widthSize = View.MeasureSpec.getSize(widthSpec);
            final int heightSize = View.MeasureSpec.getSize(heightSpec);
    
            final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED;
            final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED;
    
            final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY;
            final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY;
    
            final int unspecified = makeUnspecifiedSpec();
    
            if (exactWidth && exactHeight) {
                // in case of exact calculations for both dimensions let's use default "onMeasure" implementation
                super.onMeasure(recycler, state, widthSpec, heightSpec);
                return;
            }
    
            final boolean vertical = getOrientation() == VERTICAL;
    
            initChildDimensions(widthSize, heightSize, vertical);
    
            int width = 0;
            int height = 0;
    
            // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This
            // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the
            // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never
            // called whiles scrolling)
            recycler.clear();
    
            final int stateItemCount = state.getItemCount();
            final int adapterItemCount = getItemCount();
            // adapter always contains actual data while state might contain old data (f.e. data before the animation is
            // done). As we want to measure the view with actual data we must use data from the adapter and not from  the
            // state
            for (int i = 0; i < adapterItemCount; i++) {
                if (vertical) {
                    if (!hasChildSize) {
                        if (i < stateItemCount) {
                            // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                            // we will use previously calculated dimensions
                            measureChild(recycler, i, widthSize, unspecified, childDimensions);
                        } else {
                            logMeasureWarning(i);
                        }
                    }
                    height += childDimensions[CHILD_HEIGHT];
                    if (i == 0) {
                        width = childDimensions[CHILD_WIDTH];
                    }
                    if (hasHeightSize && height >= heightSize) {
                        break;
                    }
                } else {
                    if (!hasChildSize) {
                        if (i < stateItemCount) {
                            // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items
                            // we will use previously calculated dimensions
                            measureChild(recycler, i, unspecified, heightSize, childDimensions);
                        } else {
                            logMeasureWarning(i);
                        }
                    }
                    width += childDimensions[CHILD_WIDTH];
                    if (i == 0) {
                        height = childDimensions[CHILD_HEIGHT];
                    }
                    if (hasWidthSize && width >= widthSize) {
                        break;
                    }
                }
            }
    
            if (exactWidth) {
                width = widthSize;
            } else {
                width += getPaddingLeft() + getPaddingRight();
                if (hasWidthSize) {
                    width = Math.min(width, widthSize);
                }
            }
    
            if (exactHeight) {
                height = heightSize;
            } else {
                height += getPaddingTop() + getPaddingBottom();
                if (hasHeightSize) {
                    height = Math.min(height, heightSize);
                }
            }
    
            setMeasuredDimension(width, height);
    
            if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) {
                final boolean fit = (vertical && (!hasHeightSize || height < heightSize))
                        || (!vertical && (!hasWidthSize || width < widthSize));
    
                ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS);
            }
        }
    
        private void logMeasureWarning(int child) {
            if (BuildConfig.DEBUG) {
                Log.w("LinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." +
                        "To remove this message either use #setChildSize() method or don't run RecyclerView animations");
            }
        }
    
        private void initChildDimensions(int width, int height, boolean vertical) {
            if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) {
                // already initialized, skipping
                return;
            }
            if (vertical) {
                childDimensions[CHILD_WIDTH] = width;
                childDimensions[CHILD_HEIGHT] = childSize;
            } else {
                childDimensions[CHILD_WIDTH] = childSize;
                childDimensions[CHILD_HEIGHT] = height;
            }
        }
    
        @Override
        public void setOrientation(int orientation) {
            // might be called before the constructor of this class is called
            //noinspection ConstantConditions
            if (childDimensions != null) {
                if (getOrientation() != orientation) {
                    childDimensions[CHILD_WIDTH] = 0;
                    childDimensions[CHILD_HEIGHT] = 0;
                }
            }
            super.setOrientation(orientation);
        }
    
        public void clearChildSize() {
            hasChildSize = false;
            setChildSize(DEFAULT_CHILD_SIZE);
        }
    
        public void setChildSize(int childSize) {
            hasChildSize = true;
            if (this.childSize != childSize) {
                this.childSize = childSize;
                requestLayout();
            }
        }
    
        private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) {
            final View child;
            try {
                child = recycler.getViewForPosition(position);
            } catch (IndexOutOfBoundsException e) {
                if (BuildConfig.DEBUG) {
                    Log.w("LinearLayoutManager", "LinearLayoutManager doesn't work well with animations. Consider switching them off", e);
                }
                return;
            }
    
            final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams();
    
            final int hPadding = getPaddingLeft() + getPaddingRight();
            final int vPadding = getPaddingTop() + getPaddingBottom();
    
            final int hMargin = p.leftMargin + p.rightMargin;
            final int vMargin = p.topMargin + p.bottomMargin;
    
            // we must make insets dirty in order calculateItemDecorationsForChild to work
            makeInsetsDirty(p);
            // this method should be called before any getXxxDecorationXxx() methods
            calculateItemDecorationsForChild(child, tmpRect);
    
            final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child);
            final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child);
    
            final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally());
            final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically());
    
            child.measure(childWidthSpec, childHeightSpec);
    
            dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin;
            dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin;
    
            // as view is recycled let's not keep old measured values
            makeInsetsDirty(p);
            recycler.recycleView(child);
        }
    
        private static void makeInsetsDirty(RecyclerView.LayoutParams p) {
            if (!canMakeInsetsDirty) {
                return;
            }
            try {
                if (insetsDirtyField == null) {
                    insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty");
                    insetsDirtyField.setAccessible(true);
                }
                insetsDirtyField.set(p, true);
            } catch (NoSuchFieldException e) {
                onMakeInsertDirtyFailed();
            } catch (IllegalAccessException e) {
                onMakeInsertDirtyFailed();
            }
        }
    
        private static void onMakeInsertDirtyFailed() {
            canMakeInsetsDirty = false;
            if (BuildConfig.DEBUG) {
                Log.w("LinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect");
            }
        }
    }
    

    参考:RecyclerView高度随Item自适应

    2015-9-19

    ListView高度

    在ListView下面有一个Button,ListView设置高度为wrap_content,当ListView高度超出屏幕时,Button就不显示了。
    解决:
    给ListView增加

    android:layout_weight="1" 
    

    2015-9-21

    SearchView

    按返回键关闭SearchView

    @Override
    public void onBackPressed() {
        if ( searchView != null ) {
            if (!searchView.isIconified()) {
                searchView.setIconified(true);
            } else {
                super.onBackPressed();
            }
        }
    }
    

    参考:How do I close a SearchView programmatically?

    点击外部关闭SearchView

    public void setupUI(View view) {
    
        if(!(view instanceof SearchView)) {
    
            view.setOnTouchListener(new OnTouchListener() {
    
                public boolean onTouch(View v, MotionEvent event) {
                    searchMenuItem.collapseActionView();
                    return false;
                }
    
            });
        }
    
        //If a layout container, iterate over children and seed recursion.
        if (view instanceof ViewGroup) {
    
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
    
                View innerView = ((ViewGroup) view).getChildAt(i);
    
                setupUI(innerView);
            }
        }
    }
    

    参考:how to make searchview loose focus and collapse when clicked elsewhere on activity

    2015-9-22

    Sqlite判断记录是否存在

    Cursor cursor = db.query(……);
    if (cursor.moveToNext())
        return true;
    else
        return false;
    

    Sqlite模糊查询

    String[] columns = { COLUMN_WORD, COLUMN_DEFINITION };
    String selection = COLUMN_DEFINITION + " like ? "; 
    String[] selectionArgs = new String[]{ "%" + value + "%" };
    
    db = wordsDbHelper.getReadableDatabase();
    Cursor cursor = db.query(tableName, columns, selection, selectionArgs, null, null, COLUMN_WORD);
    

    Bundle实现Android Activity间消息的传递

    Intent intent = new Intent();  
    intent.setClass(TestBundle.this, Target.class);  
    Bundle mBundle = new Bundle();  
    mBundle.putString("Data", "ray'blog");//压入数据  
    intent.putExtras(mBundle);  
    startActivity(intent);  
    
    Bundle bundle = getIntent().getExtras();    
    String data=bundle.getString("Data");//取出数据
    

    Volley

    JsonRequest

    RequestQueue mQueue = Volley.newRequestQueue(context);  
    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://m.weather.com.cn/data/101010100.html", null,  
        new Response.Listener<JSONObject>() {  
            @Override  
            public void onResponse(JSONObject response) {  
                Log.d("TAG", response.toString());  
            }  
        }, new Response.ErrorListener() {  
            @Override  
            public void onErrorResponse(VolleyError error) {  
                Log.e("TAG", error.getMessage(), error);  
            }  
        }); 
    mQueue.add(jsonObjectRequest);  
    

    2015-9-24

    半透明背景

    res/values/color.xml

    <color name="transparent_background">#50000000</color>
    

    #5000000前两位是透明的效果参数从00—99(透明—不怎么透明),后6位是颜色的设置

    参考:android 成长 UI 学习之 Activity 透明,半透明效果的设置transparent

    圆角边框

    drawable目录里定义一个corners_bg.xml:

    <?xml version="1.0" encoding="utf-8"?>    
    <shape xmlns:android="http://schemas.android.com/apk/res/android">      
        <solid android:color="@color/translucent_background" />     //填充颜色,这里设置的半透明 
        <corners android:radius="3dp" />    //圆角弧度
    </shape>   
    

    引用:

    android:background="@drawable/corners_bg"
    

    参考:【Android】Android布局中实现圆角边框

    播放音频

    1. MediaPlayer
    • 适合播放较大文件,文件应该存储在SD卡上,而不是在资源文件里
    • 资源占用量较高,延迟时间较长。
    • 不支持多个音频同时播放

    (1)从资源文件中播放

    MediaPlayer player = new MediaPlayer.create(this,R.raw.test);
    player.stare();
    

    (2)从文件系统播放

    MediaPlayer player = new MediaPlayer();
    String path = "/sdcard/test.mp3";
    player.setDataSource(path);
    player.prepare();
    player.start();
    

    (3)从网络播放

    • 通过URI的方式:

      String path="http://**************.mp3";     //音频的网络地址
      Uri uri = Uri.parse(path);
      MediaPlayer player = new MediaPlayer.create(this,uri);
      player.start();
      
      • 通过设置数据源的方式:

        MediaPlayer player = new MediaPlayer.create();
        String path="http://**************.mp3";          //音频的网络地址
        player.setDataSource(path);
        player.prepare();
        player.start();
        

        参考:Android中的音频播放(MediaPlayer和SoundPool)

    2.SoundPool
    低延迟播放,适合播放实时音实现同时播放多个声音,如游戏中炸弹的爆炸音等小资源文件,此类音频比较适合放到资源文件夹 res/raw下和程序一起打成APK文件

    • SoundPool 使用音效池的概念来管理多个短促的音效;
    • cpu资源占用量低和反应延迟小;
    • 支持自行设置的品质、音量、播放比率等参数;
    • 异步线程,占用资源少,可以同时合成多种音效;

    修改ActionBar icon大小

    • API < 17,res/values/styles.xml :

      <item name="android:actionButtonStyle">@style/ActionButtonStyle</item>
      
      <style name="ActionButtonStyle" parent="@android:style/Widget.Holo.Light.ActionButton">
          <item name="android:minWidth">0dip</item>
          <item name="android:paddingLeft">0dip</item>
          <item name="android:paddingRight">0dip</item>                  
      </style>
      
    • API > 17,res/values-v17/styles.xml :

      <item name="android:actionButtonStyle">@style/ActionButtonStyle</item>
      
      <style name="ActionButtonStyle" parent="@android:style/Widget.Holo.Light.ActionButton">
          <item name="android:minWidth">0dip</item>
          <item name="android:paddingStart">0dip</item>
          <item name="android:paddingEnd">0dip</item>                  
      </style>
      

      参考:Is there a way to reduce the spacing between the Action Item Icons on Action Bar?

    修改actionbar的高度

    style.xml:

    <resources xmlns:android="http://schemas.android.com/apk/res/android">
        <style name="AppTheme" parent="@android:style/Theme.Holo.Light">
            <item name="android:actionBarSize">30dp</item>
        </style>
    </resources>
    

    manifest.xml:

    <application
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        android:icon="@drawable/ic_launcher"
     >
    

    参考:ActonBar介绍-修改actionbar的高度

    修改SearchView样式

    文字颜色

    输入的文字:

    int id = searchView.getContext().getResources().getIdentifier("android:id/search_src_text", null, null);
    TextView textView = (TextView) searchView.findViewById(id);
    textView.setTextColor(Color.WHITE);
    

    参考:Android中SearchView修改字体颜色

    hint提示文字:

    searchView.setQueryHint(Html.fromHtml("<font color = #999999>" + getResources().getString(R.string.search_input) + "</font>"));
    

    参考:关于SearchView的一些小细节

    自定义样式

    使用SearchViewFormatter
    用法

    new SearchViewFormatter()
            .setSearchBackGroundResource(R.drawable.my_bg)
            .setSearchIconResource(R.drawable.my_ic, true, false) //true to icon inside edittext, false to outside
            .setSearchVoiceIconResource(R.drawable.my_ic)
            .setSearchTextColorResource(R.color.my_color)
            .setSearchHintColorResource(R.color.my_color)
            .setSearchCloseIconResource(R.drawable.my_ic)
            .setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS)
            .format(mSearchView);
    

    修改Cursor颜色

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.entity_list_actions, menu);
        final SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView();
        final int textViewID = searchView.getContext().getResources().getIdentifier("android:id/search_src_text",null, null);
        final AutoCompleteTextView searchTextView = (AutoCompleteTextView) searchView.findViewById(textViewID);
        try {
            Field mCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes");
            mCursorDrawableRes.setAccessible(true);
            mCursorDrawableRes.set(searchTextView, 0); //This sets the cursor resource ID to 0 or @null which will make it visible on white background
        } catch (Exception e) {}
        return super.onCreateOptionsMenu(menu);
    }
    

    参考:Changing the cursor color in SearchView without ActionBarSherlock

    ActionBar返回上一个Activity

    在此Activity里设置

    ActionBar mActionBar = getActionBar();
    mActionBar.setDisplayHomeAsUpEnabled(true);//show back button
    

    在Manifest.xml文件中设置这个Activity的parentActivity

    <activity
        android:parentActivityName=".MainActivity"
        android:name=".BackActionBarActivity"
        android:launchMode="singleTask"
        android:label="@string/title_activity_back_action_bar">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value=".MainActivity"/>
    </activity>
    

    参考:Android ActionBar 返回上一个Activity

    修改指定Activity的Actionbar

    styles.xml:

    <style name="CustomActivityTheme" parent="AppTheme">
        <item name="android:homeAsUpIndicator">@drawable/custom_home_as_up_icon</item>
    </style>
    

    Manifest.xml:

    <activity
            android:name="com.example.CustomActivity"
            android:theme="@style/CustomActivityTheme" >
    </activity>
    

    参考:Change the actionbar homeAsUpIndicator Programamtically

    2015-9-25

    RecyclerView添加分割线

    使用RecyclerView-FlexibleDivider

    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
    recyclerView.addItemDecoration(
            new HorizontalDividerItemDecoration.Builder(this)
                .color(Color.RED)
                .size(getResources().getDimensionPixelSize(R.dimen.divider))
                .margin(getResources().getDimensionPixelSize(R.dimen.leftmargin),
                        getResources().getDimensionPixelSize(R.dimen.rightmargin))
                .build());
    

    参考:RecyclerView-FlexibleDivider——控制RecyclerView项目分割的Android类库

    2015-9-26

    Volley加载网络图片

    ImageRequest的用法

    1. 创建一个RequestQueue对象。
    2. 创建一个Request对象。
    3. 将Request对象添加到RequestQueue里面。

      RequestQueue mQueue = Volley.newRequestQueue(context);
      ImageRequest imageRequest = new ImageRequest(
          "http://developer.android.com/images/home/aw_dac.png",
          new Response.Listener<Bitmap>() {
              @Override
              public void onResponse(Bitmap response) {
                  imageView.setImageBitmap(response);
              }
          }, 0, 0, Config.RGB_565, new Response.ErrorListener() {
              @Override
              public void onErrorResponse(VolleyError error) {
                  imageView.setImageResource(R.drawable.default_image);
              }
          });
      mQueue.add(imageRequest);
      

    ImageRequest的构造函数接收六个参数,第一个参数就是图片的URL地址,这个没什么需要解释的。第二个参数是图片请求成功的回调,这里我们把返回的Bitmap参数设置到ImageView中。第三第四个参数分别用于指定允许图片最大的宽度和高度,如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示不管图片有多大,都不会进行压缩。第五个参数用于指定图片的颜色属性,Bitmap.Config下的几个常量都可以在这里使用,其中ARGB_8888可以展示最好的颜色属性,每个图片像素占据4个字节的大小,而RGB_565则表示每个图片像素占据2个字节大小。第六个参数是图片请求失败的回调,这里我们当请求失败时在ImageView中显示一张默认图片。

    ImageLoader

    ImageLoader也可以用于加载网络上的图片,并且它的内部也是使用ImageRequest来实现的,不过ImageLoader明显要比ImageRequest更加高效,因为它不仅可以帮我们对图片进行缓存,还可以过滤掉重复的链接,避免重复发送请求。

    1. 创建一个RequestQueue对象。
    2. 创建一个ImageLoader对象。
    3. 获取一个ImageListener对象。
    4. 调用ImageLoader的get()方法加载网络上的图片。

      ImageLoader imageLoader = new ImageLoader(mQueue, new ImageCache() {

      @Override
      public void putBitmap(String url, Bitmap bitmap) {
      }
      
      @Override
      public Bitmap getBitmap(String url) {
          return null;
      }
      

      });
      ImageListener listener = ImageLoader.getImageListener(imageView,

      R.drawable.default_image, R.drawable.failed_image);
      

      //imageLoader.get(“https://img-my.csdn.net/uploads/201404/13/1397393290_5765.jpeg“, listener);
      imageLoader.get(“https://img-my.csdn.net/uploads/201404/13/1397393290_5765.jpeg“, listener);

    参考:Android Volley完全解析(二),使用Volley加载网络图片

    Textview文字末尾拼接带本地图片背景文字

    textView = (TextView) findViewById(R.id.text);
    
    ImageGetter imageGetter = new ImageGetter() {
      @Override
      public Drawable getDrawable(String source) {
          int resId = Integer.parseInt(source);
          Drawable drawable = MainActivity.this.getResources()
                  .getDrawable(resId);
          drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
                  drawable.getIntrinsicHeight());
          return drawable;
      }
    };
    
    textView.setText(Html.fromHtml("我要添加一个<img src=\""+R.drawable.ic_launcher+"\">,看到了吗?", imageGetter, null));
    

    文字和背景合并插入正文中

    自定义TextDrawable,将文字内容传入,用canvas将文字和绘制的圆角矩形合并(本地图片同理)
    @Override
    public void draw(Canvas canvas) {
    paint.setColor(Color.RED);
    rectF.set(padding, -height-linsSpaceExtra, padding+rectWidth, -linsSpaceExtra);
    canvas.drawRoundRect(rectF, height/2, height/2, paint);
    //canvas.drawRect(padding, -height-linsSpaceExtra, padding+rectWidth, -linsSpaceExtra, paint);
    int baseline = (int) (rectF.top + (rectF.bottom - rectF.top - paint.getFontMetrics().bottom + paint.getFontMetrics().top) / 2 - paint.getFontMetrics().top)-2;
    paint.setColor(Color.WHITE);
    canvas.drawText(text, rectF.centerX(), baseline, paint);
    }
    参考:Textview文字末尾拼接带本地图片背景文字

    2015-10-5

    获取当前日期

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    SimpleDateFormat函数语法:
      
      G 年代标志符
      y 年
      M
      d
      h 时 在上午或下午 (1~12)
      H 时 在一天中 (0~23)
      m
      s 秒
      S 毫秒
      E 星期
      D 一年中的第几天
      F 一月中第几个星期几
      w 一年中第几个星期
      W 一月中第几个星期
      a 上午 / 下午 标记符 
      k 时 在一天中 (1~24)
      K 时 在上午或下午 (0~11)
      z 时区
    
    SimpleDateFormat myFmt=new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
    SimpleDateFormat myFmt1=new SimpleDateFormat("yy/MM/dd HH:mm"); 
    SimpleDateFormat myFmt2=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//等价于now.toLocaleString()
    SimpleDateFormat myFmt3=new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒 E ");
    SimpleDateFormat myFmt4=new SimpleDateFormat(
            "一年中的第 D 天 一年中第w个星期 一月中第W个星期 在一天中k时 z时区");
    Date now=new Date();
    System.out.println(myFmt.format(now));
    System.out.println(myFmt1.format(now));
    System.out.println(myFmt2.format(now));
    System.out.println(myFmt3.format(now));
    System.out.println(myFmt4.format(now));
    System.out.println(now.toGMTString());
    System.out.println(now.toLocaleString());
    System.out.println(now.toString());
    

    参考:SimpleDateFormat使用详解

    2015-10-7

    SQLite索引

    索引可大幅减少扫描表所需的时间。 请遵照以下准则:
    索引中的列顺序会影响性能。 WHERE 子句通常使用的列应该放在前面,然后放置 ORDER BY 子句通常使用的列。
    对于包含检索的数据的列,创建覆盖索引。
    避免重复索引。 SQLite 数据库引擎将自动为具有 UNIQUE 或 PRIMARY KEY 限制的列创建索引。

    SQLite复制表数据到已存在的表中

    insert into tagTable select * from sourceTable;  
    //tagTable     目标数据库  
    //sourceTable  源数据库  
    

    参考:Sqlite 将一张表的数据复制到另一张表中

    SQLite删除字段

    sqlite中是不支持删除列操作的,所以网上alter table table_name drop column col_name这个语句在sqlite中是无效的,而替代的方法可以如下:

    1.根据原表创建一张新表
    2.删除原表
    3.将新表重名为旧表的名称

    示例例子如下

    1.创建一张旧表Student,包含id(主码),name, tel

    create table student (
    
    id integer primary key,
    
    name text,
    
    tel text
    
    )
    

    2.给旧表插入两个值

    insert into student(id,name,tel) values(101,"Jack","110")
    
    insert into student(id,name,tel) values(102,"Rose","119")
    

    3.接下来我们删除电话这个列,首先根据student表创建一张新表teacher

    create table teacher as select id,name from student
    

    可以看到tel这一列已经没有了

    4.然后我们删除student这个表

    drop table if exists student
    

    5.将teacher这个表重命名为student

    alter table teacher rename to student
    

    结果演示:

    select * from student order by name descdesc降序, asc升序)
    

    这样就可以得到我们想要的结果了。

    参考:Sqlite删除列方法

    2015-10-9

    MediaPlayer从头播放

    ```
    if ( playerSentence != null ) {
        if ( playerSentence.isPlaying() ) {
            playerSentence.seekTo(0);
        } else {
            playerSentence.start();
        }
    }
    ```            
    

    参考:android里用MediaPlayer,当音乐现在正在播放时,点击按钮是如何让音乐从头播放

    判断数据库表是否存在

    Cursor c=db.rawQuery("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='要查询的表名'", null);  
    if (c.getInt(0)==0) {  
        return false;  
    } 
    

    参考:判断sqlite数据库中表是否存在的方法

    2015-10-10

    禁止横屏竖屏切换

    AndroidManifest.xml的需要禁止转向的Activity配置中加入android:screenOrientation="portrait"属性即可(landscape是横向,portrait是纵向)
    参考:Android禁止横屏竖屏切换

    2015-10-11

    文件下载

    OkHttp

    参考:

    从路径获取文件名

    //      方法一:  
    
        File tempFile =new File( fName.trim());  
        String fileName = tempFile.getName();            
        System.out.println("fileName = " + fileName);  
    
    //      方法二:    
        String fName = fName.trim();    
        String fileName = fName.substring(fName.lastIndexOf(File.separator)+1);            
        System.out.println("fileName = " + fileName);  
    
    //      方法三:    
        String fName = fName.trim();    
        String temp[] = fName.split("\\\\"); /**split里面必须是正则表达式,"\\"的作用是对字符串转义*/    
        String fileName = temp[temp.length-1];  
    

    参考:3种Java从文件路径中获取文件名的方法

    2016-02-28

    CardView适配

    • 不同 SDK 版本(低于 Lollipop 21)上的边距(Margin)效果

    在低版本中设置了 CardElevation 之后 CardView 会自动留出空间供阴影显示,而 Lollipop 之后则需要手动设置 Margin 边距来预留空间。
    因此,我们需要自定义一个 dimen 作为 CardView 的 Margin 值:

    创建 /res/value 和 /res/value-v21 资源文件夹于项目对应 Module 目录下,前者放置旧版本/通用的资源文件(了解的可以跳过),后者放置 21 及更高 SDK 版本的资源文件。

    在 value 内的 dimen.xml 创建一个 Dimension ( 属性),随便命个名(如 xxx_card_margin)并填入数值 0dp

    接着在 value-v21 文件夹内的 dimen.xml 创建名字相同的 Dimension,并填入你期望的预留边距(一般和 CardElevation 阴影大小相同)

    最后,在你布局中的 CardView 中设置 android:layout_margin="@dimen/xxx_card_margin"

    这样就解决了低版本中边距过大或者视觉效果不统一的问题了。

    参考:关于使用 CardView 开发过程中要注意的细节

    展开全文
  • 1.如何解决word中粘贴格式混乱问题 在我们工作中很多时候会用图片识别技术来省去打字,类似于下图 PS(百度识别网址https://ai.baidu.com/tech/ocr/general) 1.按住alt不放这样鼠标就可以选择第一排了 2.然后 按...

    之前这篇文章挺不错的,在工作学习中都是必备的小知识。重新排了一下版今天拿出来一起喵喵。

    1.如何解决word中粘贴格式混乱问题

    在我们工作中很多时候会用图片识别技术来省去打字,类似于下图
    PS(百度识别网址https://ai.baidu.com/tech/ocr/general

    在这里插入图片描述在这里插入图片描述在这里插入图片描述

    1.按住alt不放这样鼠标就可以选择第一排了
    在这里插入图片描述2.然后 按Delete 这个时候就删除了就会像上面一样。我们删除完之后又遇到一个
    在这里插入图片描述3.所以这个时候进行第三个操作,去掉所有的不需要的空格。首先选中这一段,然后按住Ctrl + H .如图
    图片

    4.清空查找内容 ,然后输入 ^p ,如下图。再点击替换。
    点击一下,修复一行,有的小伙伴说,点击全部替换行不行,会把前面的文章弄坏

    在这里插入图片描述

    2.如何解决网页中不能复制的问题

    想必大家都被不能随便复制网页的文字烦透了吧,那么来教你一招吧。看下面我们都熟悉的百度文库。
    在这里插入图片描述在这里插入图片描述1.直接按住Ctrl + P,那么整个页面就可以复制了。
    这个有个限制就是你的电脑连着打印机,不过我们办公电脑都连接着打印机,所以不用担心。

    2.如果你的电脑实在没有连接着打印机,这个时候怎么办呢,再教你一招,

    选中你要复制的内容点击搜索,
    在这里插入图片描述我现在用的是火狐浏览器,会出现在新的页面,
    在这里插入图片描述如果是谷歌浏览器,同样选中,然后右击进行搜索。那么你要复制的内容就会出现在上方,
    在这里插入图片描述其它的浏览器也类似,这样会快速让你得到你想要的内容,快来试试吧。

    3.如何实现免费pdf转worf

    pdfpai.com/

    smallpdf.com/cn

    lightpdf.com/zh/

    hipdf.cn/

    ilovepdf.com/zh-cn

    4.如何快速的打开我的电脑

    很多时候你都找不到“我的电脑”。教你一招,win + E (win 就是下图)

    在这里插入图片描述
    在这里插入图片描述打开了。

    再就是我们进入很深的文件夹,想往上回退一下,alt + ↑,前进的话alt + ↓。不用频繁的点击鼠标,时间成本太大了

    5.其它常用操作

    · Win + d 回到桌面
    · Win + L 锁屏
    · Ctrl + A 全选

    6.c盘清理(电脑上微信清理)

    我见过很多人的电脑上仅仅只有微信和网页来进行工作用,但是就这样C盘会爆满,来我们一起解决一下。

    打开电脑上的微信

    点击左下角的这个按钮(点击里面的设置)点击文件管理

    在这里插入图片描述在这里插入图片描述这个时候你会发现微信往往会在c盘安装的,那么微信里的聊天记录,图片会存储在这里,这个时候点击更改把它放到另一个地方。

    在这里插入图片描述在这里插入图片描述重启微信已经变了,这个之后就不会再占用你的C盘空间了。

    在这里插入图片描述等复制结束再检查一下C盘这个文件有没有自己想要的文件,如果没有用就可以删除了,这样你的C盘就又有大量的空间了。

    7.电脑加速

    7.1卸载软件

    方法1
    我相信很多不熟悉的删除软件还是直接把桌面上的图标放进回收站,就感觉删除了,其实不是的,正确的应该是这样
    右击桌面上图标(进入它的安装目录,这个就是它占用你电脑资源的地方,)
    在这里插入图片描述找到这个uninst.exe (大部分名称是这个)

    在这里插入图片描述
    就可以直接干净的从你电脑上清除了

    方法2 打开控制面板,

    选中你要删除的右击 就可以开始删除了。

    在这里插入图片描述点击删除

    在这里插入图片描述

    7.2.关闭开机无用启动项

    1.电脑上有360的,把一些我们没必要用的全部禁用

    图片在这里插入图片描述电脑上没有360的
    Windows10 以下的用户需要调用系统的「运行」(快捷键 Win + R)输入 「msconfig」回车
    在这里插入图片描述在这里插入图片描述对启用的进行禁用
    在这里插入图片描述

    7.3.整理磁盘碎片

    为什么要整理磁盘碎片?是因为文件被分散保存到整个磁盘的不同地方,而不是连续地保存在磁盘连续的簇中形成的。硬盘在使用一段时间后,由于反复写入和删除文件,磁盘中的空闲扇区会分散到整个磁盘中不连续的物理位置上,从而使文件不能存在连续的扇区里。

    这样,再读写文件时就需要到不同的地方去读取,增加了磁头的来回移动,降低了磁盘的访问速度。(百度百科)因此每个月的适当整理磁盘碎片,可以加速磁盘的访问速度。同样也是两种方式,系统自带和外靠软件。

    打开「此电脑」(我的电脑)后,选择要整理的盘符,点击「右键」,在「属性」里面选择「磁盘清理」就可以了。

    在这里插入图片描述
    有360的可以直接清理
    在这里插入图片描述
    本公众号分享自己从程序员小白到经历春招秋招斩获10几个offer的面试笔试经验,其中包括【Java】、【操作系统】、【计算机网络】、【设计模式】、【数据结构与算法】、【大厂面经】、【数据库】期待你加入!!!

    1.计算机网络----三次握手四次挥手
    2.梦想成真-----项目自我介绍
    3.你们要的设计模式来了
    4.震惊!来看《这份程序员面试手册》!!!
    5.一字一句教你面试“个人简介”
    6.接近30场面试分享
    7.你们要的免费书来了

    展开全文
  • 最近接到了一个小任务:使用word将数据输出,datatable也好datagridview也罢,生成才是第一要义。 一、 首先肯定是在网上找资料了,然后找到一个微软官方解释说明文档,特别好。 如何: 使用 Visual c # 自动化 ...
  • word技巧

    千次阅读 2018-03-01 21:40:22
    word 高效经典教程(整理版)目录一分钟驾驭word 高效经典教程(整理版)... 6A、基础知识... 61、度量单位... 62、WORD中文字字号与磅的对应关系... 63、字体文件格式... 7B、文本编辑... 71、快速移动文档... 72、...
  • Word文档中删除个人的隐私信息(转)[@more@]  我们知道,Word文档在保存时会自动记录各种附属信息,如作者、公司名称、最后编辑时间、批注等。由于这些信息可以被任何文档的阅读者轻松地获取,所以在某些特定的...
  • WORD经典实用!

    千次阅读 2016-07-21 17:30:31
    WORD经典实用!较全面!
  • Word基础

    千次阅读 2017-03-16 15:03:40
    系统本身兼容多种浏览器,但要获得良好的用户体验建议使用现代主流的浏览器如:Chrome(google 浏览器)、firefox(火狐浏览器)、Safari(苹果浏览器)、Opera、IE8+及WebKit内核的浏览器。如果达不到要求您可以在...
  • 逐字逐句浏览文件,并记录每个字的使用频率 程序完成后,您应该能够运行python3 word_frequency.py seneca_falls.txt并获得如下打印报告: her | 33 ********************************* all | 12 ************ ...
  • 主要是记录用jsoup解析由word变html后的压缩包的过程,并用到项目中
  • 这几年用word写各种报告论文,每每总能发现word的新大陆(写报告的时候太无聊了word都能玩起来),遂记录一下,有时候有些功能若是不知道,百度都不知道搜什么就很尴尬,我时常称奇,原来word还可以这么用!...
  • delphi word使用

    千次阅读 2009-03-06 17:10:00
    用Delphi合并Word表格中单元格//合并Word 表格中单元格procedure mergeWordCell;var WordApp: TWordApplication; WordDoc: TWordDocument; DocInx,oFileName,CfCversions,oReadOnly,AddToRctFiles,...
  • Word文档修复实战

    2017-11-28 22:15:00
    但是仍然有部分文件是不正常的,比如word文档打开后会提示你选择编码,这时无论你选择什么编码结果都是乱码。但是文件都是有大小的,凭借经验判断只是文件损坏了,应该可以恢复回来的。 今日终于成功解决这个问题,...
  • word 常用快捷键

    千次阅读 2008-11-23 15:27:00
    现在是讲效率的年代,使用Word来进行平时的办公处理也是一样,那么,我们怎样才能够在Word中“快”起来呢?那就请这位作者给我们带来的Word中快速操作的10个技巧,小编看完后也觉得相当实用。 1.快速定位到上次...
  • word编辑技巧

    千次阅读 2010-05-10 18:04:00
    1.2 编辑排版技巧(1)1.2.1 页面设置快速进行调整要对Word进行页面调整,通常大家采用的方法是选择“文件→页面设置”选项的方法进行,请问有没有更快速方便的方法呢?答:有,如果要进行“页面设置”,只需用鼠标...
  • Word 2003域应用完全手册

    千次阅读 2014-01-24 19:50:08
    Word 2003域应用完全手册  一、域应用基础  1.什么是域  简单地讲,域就是引导Word在文档中自动插入文字、图形、页码或其他信息的一组代码 。每个域都有一个唯一的名字,它具有的功能与Excel中的函数...
  • word的样式

    千次阅读 2011-04-10 09:15:00
    Word 样式可以帮助您设置格式,无论是简单的还是复杂的,您都可以有条不紊地完成。
  • (一)理解word2vec:原理篇

    千次阅读 2018-11-20 10:10:54
    为什么想起来学习word2vec呢?其实之前自己根本没有接触过NLP的知识和任务,只是最近尝试使用了embedding的方法去处理类别特征和用embedding去做推荐,发现有不错的效果。同时,自己也感触到了所掌握知识的匮乏,...
  • 功能需求要求实现文档上传、下载、查看、删除,查看没弄过,别的就不提了,以下是我记录的实现文档在线查看的方法以及效果图 实现思路 首先把word文件转为pdf,然后在用js查看pdf文件实现在线查看功能。 主要...
  • bootstrap File Input 多文件上传插件使用记录(二)删除原文件转自https://blog.csdn.net/github_36086968/article/details/72830855 在上一篇文章中,主要介绍了...
  • 利用poi实现word转换html

    万次阅读 热门讨论 2017-12-04 10:50:49
    利用POI将word转换成html实现在线阅读 转载 2015年11月03日 10:22:04 ...利用POI将word转换成html实现在线阅读 ...通过网上找资料,发现用java实现word在线阅读有以下的实现...Word=>PDF(OpenOffice+JodConverter)=
  • Word操作技巧大全

    2017-02-21 10:20:00
    Word中的“选中”方法知多少? 一、常见的“选中”方法: ü 全选(快捷键Ctrl+A):就是全部选中文档内的所有内容。这所有内容包括:文字、表格、图形、图像等可见的和不可见的标记。 ü 按住Shift+Page Down从...
  • poi操作word文档总结

    万次阅读 2014-06-05 17:18:43
    POI分段落生成纯Word动态模板并导入数据 导出数据,可以用word另存为xml格式的ftl文件,变量用${变量名}表示,然后在类中通过 freemarker去替换变量。 但是怎么导入word数据。发现如果是xml格式,数据格式很易...
  • 深度学习word2vec笔记之应用篇 声明: 1)该博文是Google专家以及多位博主所无私奉献的论文资料整理的。具体引用的资料请看参考文献。具体的版本声明也参考原文献 2)本文仅供学术交流,非商用。所以每一部分具体...
  • Microsoft office是每个办公人员所必备的办公软件,Word是我们经常要用到的文字处理软件,用来记录数据信息。如果不小心将带有重要数据信息的Word文档给删除了,那该怎么解决? Word文档被删,当然是要想办法恢复啦...
  • Word 2007 XML 解压缩格式

    千次阅读 2015-02-26 11:21:05
    本页内容 简介 Word 2007 文档包 Word XML格式的开放打包约定 ...解析Word 2007文件 ...确定Word 2007文档中的非XML部件 ...Microsoft Office Word 2007提供了一种新的默认文
  • 利用POI将word转换成html实现在线阅读

    万次阅读 热门讨论 2013-12-26 16:05:18
    利用POI将word转换成html实现在线阅读
  • 玩儿转Word 2010:22个经典小技巧大放送 2014年6月30日 17:38 在使用Word时中使用过一些小技巧,发现很不错,对文字处理方面很有帮助,现整理如下: 1、Word表格玩自动填充 在Word表格里选中要填入...
  • word排版技巧

    千次阅读 2008-04-03 09:19:00
    ★ 插入其它语种的特殊符号 用Word进行文字处理时,往往要输入很多符号。这里介绍几种常用的输入其他语种特殊符号的方法。1.插入符号法 单击“插入”菜单栏,选择“符号”命令,在里面可以选择拉丁语、希腊语等语种...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 20,088
精华内容 8,035
关键字:

word浏览记录删除