47Log

没错,就是我

  • 首页
  • 代码
  • 设计
  • 感悟
  • BUG肥猿瘦
  • 搜索

Android 开发中慎用静态变量

发表于 2016-06-25  |  分类于 感悟  |  暂无评论

静态变量,在Java中是当类被卸载的时候会被清空,一般情况下被卸载的时候是JVM结束的时候。但是在Android这里,使用的是Dalvik虚拟机,每个应用启动的时候都会实例化一个虚拟机对象,应用结束的时候就会消失,然后静态变量就会被回收了。



常见场景

应用后台的时候,被回收了,当用户恢复应用的时候,静态变量就会被重置为空,就会出现问题.

阅读全文 »

自定义按钮状态(Custom States)

发表于 2016-06-25  |  分类于 分享  |  暂无评论

Custom States
作者详细介绍了怎么自定义按钮的状态(不是按钮状态的响应哦) 原来这东西还可以自定义的

阅读全文 »

更改Android Studio 默认打开文件方式

发表于 2016-06-25  |  分类于 代码  |  暂无评论

有时候要更改Android Studio默认打开方式 setting 里面找file types 就可以了
具体详情看这里

阅读全文 »

提交XLog到JCenter

发表于 2016-06-25  |  分类于 代码  |  暂无评论

为了XLog的使用方便,我决定把XLog提交到jcenter
参考了这篇文章

将自己的开源项目提交到JCENTER


这篇文章是可行的,需要修改的地方是把项目的build.gradle 修改为以下内容

// Top-level build file where you can add configuration options common to all sub-projects/modules.


buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.1.2'
        classpath 'com.github.dcendents:android-maven-plugin:1.2'
        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }




}


allprojects {
    repositories {
        jcenter()
    }


}



需要把build:gradle:1.1.0 改为 1.1.2 ,因为1.1.0有bug,会出现错误

阅读全文 »

分享一段小代码canScrollList

发表于 2016-06-25  |  分类于 代码  |  暂无评论

最近在写判断ListView是否可以滚动的代码,忽然发现一个函数
“canScrollList“
它的代码是这样的

/**
* Check if the items in the list can be scrolled in a certain direction.
*
* @param direction Negative to check scrolling up, positive to check
*            scrolling down.
* @return true if the list can be scrolled in the specified direction,
*         false otherwise.
* @see #scrollListBy(int)
*/
public boolean canScrollList(int direction) {
        final int childCount = getChildCount();
        if (childCount == 0) {
            return false;
        }


        final int firstPosition = mFirstPosition;
        final Rect listPadding = mListPadding;
        if (direction > 0) {
            final int lastBottom = getChildAt(childCount - 1).getBottom();
            final int lastPosition = firstPosition + childCount;
            return lastPosition < mItemCount || lastBottom > getHeight() - listPadding.bottom;
        } else {
            final int firstTop = getChildAt(0).getTop();
            return firstPosition > 0 || firstTop < listPadding.top;
        }
    }



看到这里大家都明白了,这个代码虽然是API19才能用,但是是可以提取出来兼容到低版本的
于是,我就做了这样一个兼容的方法



    /**
     * 判断是否可以滚动的兼容函数
     * @param listView
     * @param direction 方向,大于0就是向下的方向
     * @return
     */
    public static boolean canScrollList(AbsListView listView, int direction) {
        if (Build.VERSION.SDK_INT >= 19) return listView.canScrollList(direction);
        else {
            final int childCount = listView.getChildCount();
            if (childCount == 0) {//列表中没有一个子对象的时候,当然是不能滚动啦
                return false;
            }
            final int firstPosition = listView.getFirstVisiblePosition();
            if (direction > 0) {//判断方向,大于零的就是表示判断能否往下滚动
                final int lastBottom = listView.getChildAt(childCount - 1).getBottom();
                final int lastPosition = firstPosition + childCount;
                //最右一个item的位置小宇总位数的时候可以滚动
                // 最后一个item的底部高度如果比减去padding之后的
                // listview底部还要高的那就是在屏幕外,就是可以滚动的
                return lastPosition < listView.getCount() || lastBottom > listView.getHeight() - listView.getListPaddingBottom();
            } else {
                final int firstTop = listView.getChildAt(0).getTop();
                return firstPosition > 0 || firstTop < listView.getListPaddingTop();
            }
        }
    }

注意这个东西只能在item 等高的时候判断

阅读全文 »

gradle 打包工具

发表于 2016-06-25  |  分类于 代码  |  暂无评论

介绍一个很好用的打包工具gradle-packer-plugin



很多时候我们希望给我们的APK命名为:


"appname_buildtype_buildVersion_versionName_buildingTime.apk"

这样格式方便我们给运营让我们可以一眼识别这个是什么时候发布的APK,但是这样的东西如果让我们手动编写肯定会非常麻烦,gradle-packer-plugin就是用来干这个事情的,当然还支持多渠道打包。


应用的使用方法github上都有介绍,在这里我说说这个插件如何放到run上,让我们可以一键打包

注:下面的方法是要按照GitHub的方法配置完成之后才能做的

图一
图二



  1. 图上第一个圈圈代表你要打包的项目(选择的时候是不能选目录的,只能选build.gradle,把那一部分的路径删除就可以了)
  2. 添加任务“archiveApkRelease”
  3. 添加参数,指定需要打包的市场名单 -Pmarket=markets.txt (我的市场名单在根目录)
  4. 点击+号,添加一个运行前的清理任务(实验证明,没有这个也是可以的) ,配置重复上面,不懂的地方在于添加的任务为"clean" 详情看图二
  5. 点击确定
  6. 最后我们就可以点击run来打包应用啦图三
阅读全文 »

XLog介绍

发表于 2016-06-25  |  分类于 代码  |  暂无评论

XLog

XLog 是一个帮助Android开发打印的调试信息的工具类

  1. 自动填充TAG(格式为类名+函数名+方法名+行号)
  2. 无需考虑print string为null的事情
  3. 一个方法取消所有打印
    

    平常使用 Log
    Log.i(TAG,print string);

我们需要构建TAG,并且还要防止 print string 为null 这种情况


在xlog,不需要考虑这些

    XLog.i(print object);

不需要tag ,自动默认的tag就是当前调用的类和行数,打印的是object.toString()内容,不需要把基本类型弄成
string,自动判断是否为空,为空时,会打印报空的问题。

    XLog.setLogState(false);

关闭所有的debug
也可以单独关闭每一个

    XLog.allowI = false;




Thanks for

xutils 一个开源框架,xLog参考了其中LogUtils大部分的思路,改进了流程





阅读全文 »

PhotoCroper介绍使用

发表于 2016-06-25  |  分类于 代码  |  暂无评论

PhotoCroper

A helper to use system gallery or camera take Photo
used code
fix some problem

  1. 


    • can not save crop pitcure
    • can not picker some photo from gallery
    • some api review
      

      # How to use
    • new helper and set CropParam
    CropHelper cropHelper = new CropHelper(this);
    cropHelper.setCropParams(new CropParams());
    
    1. insert handle
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    cropHelper.handleResult(requestCode,resultCode,data);
    }
    1. handle result
     cropHelper.setCropListener(new CropListener() {
            @Override
    public void onPhotoCropped(Uri uri) {
    Log.d(TAG, "Crop Uri in path: " + uri.getPath());
    Toast.makeText(TestActivity.this, "Photo cropped!", Toast.LENGTH_LONG).show();
    mImageView.setImageBitmap(cropHelper.getCropBitmap());
    }
    

    @Override
    public void onCropCancel() {
    Toast.makeText(TestActivity.this,"cancel",Toast.LENGTH_SHORT).show();
    }
    

    @Override
    public void onCropFailed(String message) {
    Toast.makeText(TestActivity.this,message+"",Toast.LENGTH_SHORT).show();
    }
    });

    


    Thanks for

  2. Android大图片裁剪终极解决方案(上:原理分析)

  3. Code
    

    


阅读全文 »

关于Android菜单上的记录

发表于 2016-06-25  |  分类于 代码  |  暂无评论



最近在做分享上一下事情,想遵守Android Design 规范做一个分享的菜单但是遇到一些问题


1. Android上ShareProvider 不支持捕获分享(也许是我没找到),于是就不能针对某个分享项做优化
2. 抛弃ShareProvider使用,单纯的使用Android的菜单分享选项,这样虽然解决了不能捕获的问题,但是也是带了一个问题,就是无法用比较少的代码来完成分享。(发现弹出对话框的分享方式是最简单省事的)

最后权衡了知识和利弊之后只能做个简单的分享类,在CreateMenu 的时候addSubMenu ,
然后在OnMenuOptionSelected() handle 操作就可以了。


代码如下

package com.changheng.app.ui;


import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;


import com.changheng.app.R;


import cn.sharesdk.framework.Platform;
import cn.sharesdk.framework.ShareSDK;
import cn.sharesdk.wechat.favorite.WechatFavorite;
import cn.sharesdk.wechat.friends.Wechat;
import cn.sharesdk.wechat.moments.WechatMoments;


/**
 * Created With Android Studio
 * User 47
 * Date 2015/2/13
 * Time 14:29
 * 分享助手
 */
public class ShareMenuHelper  {
    private Context mContext;
    private SubMenu mSubMenu;
    private static ShareItem[] SHARE_ITEM = {
            new ShareItem(R.drawable.logo_wechat,
                    R.string.wechat,
                    ShareSDK.getPlatform(Wechat.NAME)),
            new ShareItem(R.drawable.logo_wechatfavorite,
                    R.string.wechatfavorite,
                    ShareSDK.getPlatform(WechatFavorite.NAME)),
            new ShareItem(R.drawable.logo_wechatmoments,
                    R.string.wechatmoments,
                    ShareSDK.getPlatform(WechatMoments.NAME))
    };


    public ShareMenuHelper(Context context,SubMenu subMenu){
        mContext = context;
        mSubMenu = subMenu;
    }


    public void init(){
        int i = 0;
        for (ShareItem shareItem : SHARE_ITEM){
            SubMenu subMenu = mSubMenu.addSubMenu(
                    Menu.NONE,shareItem.id = i++,Menu.NONE,
                    shareItem.titleRes);
            subMenu.setIcon(shareItem.iconId);
        }
    }




    public void handlerMenuSelected(MenuItem menuItem, Platform.ShareParams shareParams){
        int id = menuItem.getItemId();
        for (ShareItem shareItem : SHARE_ITEM) {
            if (id == shareItem.id) doShare(shareItem,shareParams);
        }
    }


    private void doShare(ShareItem shareItem, Platform.ShareParams shareParams) {
        shareItem.platform.share(shareParams);
    }


    public static class ShareItem{
        public int id;
        public int iconId;
        public int titleRes;
        public Platform platform;


        public ShareItem( int iconId, int titleRes, Platform platform) {
            this.iconId = iconId;
            this.titleRes = titleRes;
            this.platform = platform;
        }
    }


}





这个东西主要比较纠结的地方在于,如何获取submenu。找了好久才发现代码是这样的

menuItem.getSubMenu();
阅读全文 »

一个使用Toolbar的例子

发表于 2016-06-25  |  分类于 代码  |  暂无评论
<android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:background="?attr/colorPrimary"
            app:titleTextAppearance="@style/MyTitleTextStyle"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="?attr/actionBarSize"/>

about apptheme

app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"

more
这个使用的主题必须要和Actionbar使用的主题对应,比如的我用的Light.DarkAction.那么这个主题必须也要是这个,不然会连带ActionBar那边的按钮也会出现问题


about titleTextAppearance

app:titleTextAppearance="@style/MyTitleTextStyle"
阅读全文 »

ActionBar 主题修改

发表于 2016-06-25  |  分类于 代码  |  暂无评论

下面XML演示如何在v21 appcompact上修改Actionbar title,颜色,字体颜色

<!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">#FF3333</item>
        <!--   darker variant for the status bar and contextual app bars -->
        <item name="colorPrimaryDark">#FF3333</item>
        <!--   theme UI controls like checkboxes and text fields -->
        <item name="colorAccent">#FFF</item>
    </style>


    <!--<style name="pallte_btn">-->
        <!--<item name="android:layout_width">60dp</item>-->
        <!--<item name="android:layout_height">60dp</item>-->
    <!--</style>-->


    <!--<style name="tabStyle">-->
    <!--</style>-->


    <style name="TestTheme" parent="AppTheme">
        <item name="actionBarStyle">@style/actionBarStyle</item>
        <item name="actionMenuTextAppearance">@style/myActionMenuTextAppearance</item>
    </style>


    <style name="myActionMenuTextAppearance" parent="@style/TextAppearance.AppCompat.Widget.ActionBar.Menu">
        <item name="android:textSize">20sp</item>
        <item name="android:shadowRadius">4</item>
        <item name="android:shadowColor">#000</item>
        <item name="android:shadowDx">2</item>
        <item name="android:shadowDy">2</item>
    </style>


    <style name="actionBarStyle" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
        <item name="titleTextStyle">@style/MyTitleTextStyle</item>
    </style>


    <style name="MyTitleTextStyle" parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
        <item name="android:shadowRadius">4</item>
        <item name="android:shadowColor">#000</item>
        <item name="android:shadowDx">2</item>
        <item name="android:shadowDy">2</item>
    </style>
阅读全文 »

Git 工作流

发表于 2016-06-16  |  分类于 感悟  |  暂无评论

场景

项目已经上线,有用户反馈说我们的程序有bug。同时,产品狗扔下了两个发布时间不一样的需求修复,拍照功能和美颜功能,拍照功能要下一版本发布,美颜功能要下下版本发布

问题

如何修复Bug同时,不影响新功能的开发,并且把两次的需求隔离开来.

解决方案

建立完善的工作流

项目初始化的时候我们新建三个分支,分别是release ,develop和master。同时,项目发布的时候我们要建立tag,通过tag我们找回对应版本的代码。

release 分支

保存可以发布的或者已经发布的代码分支

master 分支

保存着开发好测试过的代码,每次develop上开发好的功能都可以合并到master分支

develop 分支

我们的开发分支,开发过程中分享的代码

有这三个分支的前提下我们就可以从容的应对需求了。

修复线上的bug

从v1.0.0(假设出现问题的就是v1.0.0版本) tag 中 checkout出新的分支,hotfix_xxx, 修复好之后直接合并回去发布

开发拍照功能

直接在develop上开发,开发完成后合并到master上,测试过后就可以合并到release上了

开发美颜功能

从master中新建一个分支,叫feature_pretty ,在这个分支上同步开发,功能要上线的时候再合并到release分支上

这样就比较好的方式解决了上面这个问题了

参考

  • Git Pro
阅读全文 »

Git快速入门

发表于 2016-06-14  |  分类于 代码  |  暂无评论

初始化项目

直接clone已经有的项目

git clone https://github.com/hangox/git-use-demo.git

或者初始化本地项目

移动到项目的地址

cd /Users/hangox/Desktop/AndroidWorkspace/GitUseDemo

再敲入

git init

这个时候敲入git status 就会看到

On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .gitignore
    .idea/
    app/
    build.gradle
    gradle.properties
    gradle/
    gradlew
    gradlew.bat
    settings.gradle

nothing added to commit but untracked files present (use "git add" to track)

这是时候就会表示成功了

设置代码提交者信息

如果不设置代码提交者信息当然也可以提交,但是谁不希望提交上去的代码有个有个牛逼的名字呢😈

设置名字:

git config user.name "NiuBi"

设置邮箱:(出问题了人家可以找到你😂)

git config user.email "hangox.liang@gmail.com"

如果你想所有项目都是应用这个名字和邮箱只需要加上参数-- global
比如:

git config --global user.name "NiuBi"
git config user.email "hangox.liang@gmail.com"

要想查看现在的config信息可以这样

git config --list

你就会看到

user.name=47
user.email=liang.hanguang93@gmail.com
core.autocrlf=input
core.excludesfile=/Users/hangox/.gitignore_global
core.ignorecase=false
difftool.sourcetree.cmd=opendiff "$LOCAL" "$REMOTE"
difftool.sourcetree.path=
mergetool.sourcetree.cmd=/Applications/SourceTree.app/Contents/Resources/opendiff-w.sh "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED"
mergetool.sourcetree.trustexitcode=true
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.precomposeunicode=true
user.name=NiuBi

如果你发现了两个user.name和user.email ,这是其中一个是global的,一个是项目的而已

当然git config 还有很多可以设置的,包括合并工具什么的,自己挖掘吧!

编写.gitignore

什么是.gitignore

.gitignore 就好像它的名字一样

告诉 git忽略一些不需要的文件

.gitignore 编写规则

项目文件结构是这样的

.
├── GitUseDemo.iml
├── app
│   ├── app.iml
│   ├── build
│   ├── build.gradle
│   ├── libs
│   ├── proguard-rules.pro
│   └── src
├── build
│   └── generated
├── build.gradle
├── gradle
│   └── wrapper
├── gradle.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle

我想要忽略build文件夹,.iml 结尾文件和local.properties

#忽略build 文件夹
/build
#忽略local.properties 文件
local.properties
#忽略.xml 结尾的文件,使用正则
*.iml
#不忽略GitUseDemo.xml,这里只是为了演示,其实应该忽略的
!GitUseDemo.iml
#gradle 缓存文件,不需要提交
.gradle
#idea 的配置文件,不需要提交
.idea


这个时候我们敲入git status

On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .gitignore
    GitUseDemo.iml
    app/
    build.gradle
    gradle.properties
    gradle/
    gradlew
    gradlew.bat
    settings.gradle

nothing added to commit but untracked files present (use "git add" to track)

可以看到不需要的东西都被忽略了

添加需要提交的文件到暂存区

添加当前文件夹所有文件

git add .

移除追踪的文件

##去掉--cached就是同时删除文件
git rm --cached build.gradle

使用简单的匹配添加文件

添加后缀为.gradle的文件

git add *.gradle

提交

简单的提交方法

git commit -m 'first commit'

如果你写的东西比较长,可以这样

git commit 

这样将会出现一个vim 编辑器来编写,像这样


推送到远端

以上我们做的都这是发生在本地(这也是为什么Git快的原因了),如果需要和小伙伴们合作的话,就需要推送到远端了。

如果你是clone下来的可以跳过以下这一步了

  • 添加远端
# 添加远端,origin 只是一个名字而已,只是默认都是叫这个
git remote add origin https://github.com/hangox/git-use-demo.git

# 查看远端信息
git remote -v

  • 更新以下本地的代码(每次同步之前建议这样做,虽然我老是忘记/(ㄒoㄒ)/~~)
# pull 其实是包含了两步操作,从remote 中fetch(拿来)代码,和当前分支(也就是master) 
# merge(合并),如果你们公司使用的是rebase的话,就不能这样干了。但是大部分情况下都是
# 使用merge的
git pull origin master
  • 上传代码
# git push <remote-name> <remote-branch-name>
git push origin master

等待片刻,代码就上传完毕,你就可以和小伙伴们愉快的玩耍了O(∩_∩)O~~

参考

  • Git Pro
阅读全文 »

Android App网络通信安全

发表于 2016-06-13  |  分类于 代码  |  暂无评论




前提

没有100%的安全,我们做的只是提高破解的成本,当成本比破解得到的金钱高的时候,自然没有人愿意破解了



场景

我们的接口协议被破,破解者通过程序直接刷我们的新增用户,通过邀请机制获取积分。



问题引出

如何保证和接口通信的就是我们的客户端,而不是别人,或者更深的一层,如何保证客户端和服务器通信的就是彼此,没有第三方。



根据问题得出要做到以下几点

  1. 数据不被篡改
  2. 数据只有双方才能看懂
  3. 数据具有时效性

如何保证数据不被篡改

加上数据完整性的校验签名(前提是只有客户端才能生成这个检验签名)

如何使数据只有双方才能看懂

使用加密算法(前提是只有客户端才能生成校验签名)


能做到以上两点,网络通信就没有基本问题了。
但是,以上两点都有个非常重要的前提,也是一个非常困难的前提

只有客户端能生成一些东西

数据具有时效性

加入加密过的时间戳(前提是只有客户端才能生成这个时间戳)

如何保证客户端生成一些东西

使用C写生成的逻辑,不使用Java

Java是非常容易反编译的语言,即使混淆了,一些常量也是无法混淆的,所以当反编译的时候非常容易通过搜索来找到对应的常量。但是C不一样,C写的东西反编译后是汇编代码,汇编代码并不是多少人可以看得懂的,一定程度上提高了破解的成本。

对调用者进行校验

Android的so库,如果不对使用者进行校验,使用的人可以新建一个一样包名的应用,然后调用我们的so库,一样可以知道对应的输入输出。我们这里在C库里做了对签名的校验,如果发现签名不是我们的,就会用另外一条密钥对数据进行加密。

总结

要想网络协议不被破解,App必须要能够有一个黑匣子一样的东西,让人无法破解,最常用的办法就是把安全逻辑的代码写入到C库中,并对C库进行校验。

延伸和拓展

  1. 对C库代码进行混淆,提高反编译难度
  2. 没有100%的安全,做好行为监控才是王道,如果一个用户压根就没有点开过这个页面,却收到了这个页面的请求,这点足够怀疑这个请求的来源了。
阅读全文 »

Android 自定义按钮状态

发表于 2016-02-12  |  分类于 代码  |  暂无评论

需求描述:

最近做个项目,有一个按钮,有下载,安装,点击,安装中,下载中和使用这六个不同的状态,每个状态对应的按钮颜色也是不一样的,加上按下去的变色,大概有12种状态。

解决方案:

  • 6种状态对应不一样的background,设置为不同的状态的时候,换上对应的背景。
  • 自定义按钮状态

选择方案:

其实第二种和第一种的本质区别就是把所有的状态的文件写到一个xml里,但是更优雅,果断使用第二种

方案实现:

  • 定义自己的状态
  • 重写控件的onCreateDrawableState(int extraSpace) 告诉系统你有多少种状态

就这两个步骤,非常简单

定义自己的状态
   <declare-styleable name="ColorStateButton">
        <attr name="state_download" format="boolean"/>
        <attr name="state_downloading" format="boolean"/>
        <attr name="state_install" format="boolean"/>
        <attr name="state_installing" format="boolean"/>
        <attr name="state_open" format="boolean"/>
    </declare-styleable>

其实我想试试可不可以直接用数字类型的,当时时间比较紧急,就没试了,有兴趣的可以试试

重写onCreateDrawableState
 @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        if (mColorStatus != null && mColorStatus.getMergeState() != null && isEnabled()) {
            final int[] drawableStatus = super.onCreateDrawableState(extraSpace + mColorStatus.getMergeState().length);//告诉系统你要添加拓展的状态数量
            //告诉系统你要合并的状态
            mergeDrawableStates(drawableStatus, mColorStatus.getMergeState());
//            printList(drawableStatus);
            return drawableStatus;
        }¡
        return super.onCreateDrawableState(extraSpace);
    }

详细代码:

按钮的background
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto">
    <!-- 需要下载状态-->
    <item
        android:drawable="@drawable/ic_color_button_download_normal"
        android:state_pressed="false"
        app:state_download="true"/>
    <item
        android:drawable="@drawable/ic_color_button_download_pressed"
        android:state_pressed="true"
        app:state_download="true"/>

    <!--需要安装状态-->
    <item
        android:drawable="@drawable/ic_color_button_install"
        android:state_pressed="false"
        app:state_install="true"/>
    <item
        android:drawable="@drawable/ic_color_button_install_pressed"
        android:state_pressed="true"
        app:state_install="true"/>
    <!--按钮不可用-->
    <item
        android:drawable="@drawable/ic_color_button_disable"
        android:state_enabled="false"/>

    <item
        android:drawable="@drawable/ic_color_button_normal_pressed"
        android:state_pressed="true"/>
    <!--正常状态-->
    <item
        android:drawable="@drawable/ic_color_button_normal"/>
</selector>
按钮的源码
package com.bingwish.bibao.ui.fragment;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.annotation.StringRes;
import android.util.AttributeSet;
import android.widget.Button;

import com.bingwish.bibao.R;
import com.hangox.xlog.XLog;

/**
 * Created With Android Studio
 * User hangox
 * Date 16/1/11
 * Time 下午8:58
 * 下载显示按钮
 */
public class ColorStateButton extends Button {
    public static final int[] STATUS_DOWNLOAD = {R.attr.state_download};
    public static final int[] STATUS_INSTALL = {R.attr.state_install};
    private ColorStatus mColorStatus = ColorStatus.DOWNLOAD;

    public ColorStateButton(Context context) {
        super(context);
        init(context);
    }

    public ColorStateButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ColorStateButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    private void init(Context context) {
        setColorState(ColorStatus.DOWNLOAD);
    }

    /***
     * 设置颜色类型
     *
     * @param colorState
     */
    public void setColorState(ColorStatus colorState) {
        setText(colorState.getButtonTextRes());
        switch (colorState) {
            case DOWNLOADING:
            case INSTALLING:
                setEnabled(false);
                return;
        }
        setEnabled(true);
        mColorStatus = colorState;
        refreshDrawableState();
        invalidate();
    }

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        if (mColorStatus != null && mColorStatus.getMergeState() != null && isEnabled()) {
            final int[] drawableStatus = super.onCreateDrawableState(extraSpace + mColorStatus.getMergeState().length);
            mergeDrawableStates(drawableStatus, mColorStatus.getMergeState());
//            printList(drawableStatus);
            return drawableStatus;
        }
        return super.onCreateDrawableState(extraSpace);
    }

    private void printList(int[] ints) {
        StringBuilder builder = new StringBuilder();
        for (int state : ints) {
            builder.append(state).append("#");
        }

        XLog.i(builder.toString());
    }

    public enum ColorStatus {
        DOWNLOAD(STATUS_DOWNLOAD, R.string.state_download),
        CLICK(STATUS_INSTALL, R.string.state_click),
        OPEN(null, R.string.state_open),
        DOWNLOADING(null, R.string.state_downloading),
        INSTALL(STATUS_INSTALL, R.string.state_install),
        INSTALLING(null, R.string.state_installing);
        int[] mergeState;
        @StringRes
        int mButtonTextRes;

        ColorStatus(int[] statusDownload, @StringRes int buttonTextRes) {
            mergeState = statusDownload;
            mButtonTextRes = buttonTextRes;
        }

        public int getButtonTextRes() {
            return mButtonTextRes;
        }

        public int[] getMergeState() {
            return mergeState;
        }
    }

}
阅读全文 »

Sock代理转换为http 代理

发表于 2016-02-10  |  分类于 代码  |  暂无评论

为了应对日益强大的防火墙,大部分人开始专用ShadowSock 作为代理,但是ShadowSock并不支持http 代理,而很多软件非常依赖于http 代理,比如Android SDK ,终端等,所以我们需要Privoxy

以下以Mac系统为例演示安装过程

安装
brew install proxivy

如果你看到这个了

表示你已经安装成功了

配置

配置其实很简单,上面的截图已经告诉你配置文件在哪里,就是/usr/local/etc/privoxy/的config 文件
敲入

vi /usr/local/etc/privoxy/config

在最下面加入

# 监听端口为8118,如果需要其他设备访问,则需要将ip改为路由器的IP 192.168.1.1 或 0.0.0.0 或者直接 :8118
listen-address  0.0.0.0:8118 #如果要使用默认配置,可以不需要这行
forward-socks5 / 127.0.0.1:1080 .
#forward-socks5 表示监听sock5 ,127.0.0.1:1080是本地shadowsock 地址, "." 表示监听所有的URL

配置完成后如图


启动

还记得安装成功后的截图不?

这是直接运行,但是关机后就没了

privoxy /usr/local/etc/privoxy/config

下面是加入系统启动项

ln -sfv /usr/local/opt/privoxy/*.plist ~/Library/LaunchAgents
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.privoxy.plist

如果你选择了其中一个敲入,并没有任何提示,那就是表示成功了

验证
  • 首先验证这个进程是否已经启动了
ps aux  | grep privoxy

截图如下:


  • 验证provixy 是否在监听8118这个端口
netstat -an | grep 8118

结果如下:

这样就是表示正在监听8118端口了

阅读全文 »

Linux 笔记

发表于 2016-02-10  |  分类于 代码  |  暂无评论

内核查看

uname -a

上传或者下载文件

#下载
scp -r -P {SSH端口号} {用户名}@{主机IP地址}:{主机文件目录}   {本地文件目录}

#上传
scp -r -P {SSH端口号}  {本地文件目录} {用户名}@{主机IP地址}:{主机文件目录}

查看当前的IP地址

ip addr show

开启某个服务开机启动

sudo chkconfig [服务名] on
sudo chkconfig docker on

Centos7 添加服务器端口

firewall-cmd --zone=public --add-port=80/tcp --permanent

Firewall 移除port

firewall-cmd --zone=public  --remove-port=19999/tcp --permanent

关闭SELinux

修改/etc/selinux/config 文件
将SELINUX=enforcing改为SELINUX=disabled

添加环境变量

不要直接修改/etc/profile 文件,把需要执行的代码加入到xx.sh 文件中,移动到/etc/profile.d/中就好了

阅读全文 »

文章导航

1 … 4 5
avatar

hangox

137 文章
6 分类
44 标签
RSS
友情链接
© 2019 - 2022 hangox
Powered by WordPress
Theme NexT WP