| How do I uninstall or remove unwanted plugins?

  • npm uninstall [GRUNT_PLUGIN] --save-dev, this will remove the plugin from
    your package.json and from node_module
  • delete the dependencies in package.json mannually and run npm prune

| 异步任务,需要在task body中调用this.async()来开启。
var done = this.async()
Note that passing false to the done() function tells Grunt that the task has
failed.

| 获取configuration中的属性值
grunt.config('meta.name')
or
grunt.config(['meta', 'name'])

| 验证configuration中是否存在property:
grunt.config.requires('meta.name')
or
grunt.config.requires(['meta', 'name'])

| Tasks can be dependent on the successful execution of other tasks.
grunt.task.requires('foo')

| Inside a task, you can run other tasks.
grunt.task.run('bar', 'baz')
or
grunt.task.run(['bar', 'baz'])

| 一旦一个任务返回了false,就会abort了。通过添加参数--force来强制向后继续执行。

| 执行多个任务用空格分开:
grunt foo bar 和下面的代码一样

1
2
grunt foo
grunt bar

| Creating Tasks

  • Alias Task;

    1
    2
    3
    grunt.registerTask(taskName, [description, ], taskList);
    // eg.
    grunt.registerTask("default", ['jshint', 'qunit', 'concat:dist', 'uglify:dist']);
  • Multi Task;
    When a multi task is run, Grunt looks for a property of the same in the Grunt Configuration.

    1
    2
    3
    4
    5
    6
    grunt.registerMultiTask(taskName, [description, ], taskFunction);

    // eg.
    grunt.registerMultiTask('log', 'log stuff.', function(){
    grunt.log.write(this.target + ":" + this.data);
    });
  • Basic Task;
    When a basic task is run, Grunt doesn’t looks at the configuration and environment.
    通过冒号来传递参数:例如针对下面的声明,通过grunt log:ARG1:ARG2来调用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    grunt.registerTask(taskName, [description, ], taskFunction);

    // eg.
    grunt.registerTask('foo', 'A sample task that logs stuff.', function(arg1, arg2){
    if(arguments.length === 0) {
    grunt.log.write(this.name + ", no args.");
    } else {
    grunt.log.write(this.name + ", arg1=" + arg1 + ", arg2=" + arg2);
    }
    });
  • Custom Task;

| eg.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %>*/\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
},
concat: {
options: {
separator: ";"
},
dist: {
src: ['src/**/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},
qunit: {
files: ['test/**/*.html']
},
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
globals: {
jQuery: true,
console: true,
module: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint', 'qunit']
}

});

// grunt.loadNpmTasks('grunt-contrib-uglify');
// grunt.loadNpmTasks('grunt-contrib-concat');
// grunt.loadNpmTasks('grunt-contrib-qunit');
// grunt.loadNpmTasks('grunt-contrib-jshint');
// grunt.loadNpmTasks('grunt-contrib-watch');

require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

grunt.registerTask('test', ['jshint', 'qunit']);
grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']);

grunt.registerTask('log', 'log some stuff', function() {
grunt.log.write('Logging some stuff ...').error();
});
};

| importing external data

  • grunt.file.readJSON()
  • grunt.file.readYAML()

| Template

  • <%= prop.subprop %> Expand to the value of prop.subprop in the config,
    regardless of type. Templates like this can be used to reference not only
    String values, but also arrays and other objects.
  • <% %> Execute arbitrary inline JavaScript code. This is useful with control
    flow or looping.
    https://gruntjs.com/configuring-tasks#templates

| Global Patterns.
All most people need to know is that foo/*.js will match all files
ending with .js in the foo/ subdirectory, but foo/**/*.js will match
all files ending with .js in the foo/ subdirectory and all of its
subdirectories.

| Specifying both a task and target like grunt concat:foo or grunt concat:bar
will process just the specified target’s configuration, while run grunt concat
will iterate over all targets, processing each in turn.

1
2
3
4
5
6
7
8
grunt.initConfig({
concat: {
foo: {
},
bar: {
}
}
});

| Define a configuration for each of the tasks we mentioned.
Note: The configuration object for a plugins lives as a property on the
configuration object, that offen shares the same name as its plugin.
eg. grunt-contrib-concat => concat

| 5个插件

  • grunt-contrib-watch: check the code at every change you perform;
  • grunt-contrib-jshint: ensure best practices, or check the code behaviors;
  • grunt-contrib-uglify: 创建一个minified的version;
  • grunt-contrib-qunit: 测试你的project;
  • grunt-contrib-concat: 多个文件合并成一个文件;

| The typical folder structure features the following folders: src, dist, and test.

  • The src folder (sometimes called app) contains the source code of the library as you author it.
  • The dist folder (sometimes called build) contains the distribution, a minified version of the source code.
  • the test folder contains the code to test the project.
    https://gruntjs.com/sample-gruntfile

| Grunt and Grunt plugins should be defined as devDependencies in your project’s
package.json.
please use: npm install $package --save-dev
https://gruntjs.com/installing-grunt

| load all grunt tasks

1
2
3
4
5
6
// load all grunt tasks
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

// 上面的一句话就替代了很多次的类似下面的调用
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');

注意:matchdep需要在package.json中的devDependencies中添加:
"matchdep": "latest"

| A GruntFile is comprised of the following parts:

  • The wrapper function
  • Project and task configuration
  • Loading Grunt plugins and tasks
  • Custom tasks

| Because <% %> template strings may reference any config properties,
configuration data like filepaths and file lists may be specified this way
to reduce repetition.

获取日期字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// [javascript Date format(js日期格式化)(转) - 先锋之客 - 博客园](https://www.cnblogs.com/xianfengzhike/p/9656382.html)
function format(date, format) {
let o = {
"M+": date.getMonth() + 1, // month
"d+": date.getDate(), // day
"h+": date.getHours(), // hour
"m+": date.getMinutes(), // minute
"s+": date.getSeconds(), // second
"q+": Math.floor((date.getMonth() + 3) / 3), // quarter
S: date.getMilliseconds() // millisecond
};
if (/(y+)/.test(format)) {
format = format.replace(
RegExp.$1,
(date.getFullYear() + "").substr(4 - RegExp.$1.length)
);
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(format)) {
format = format.replace(
RegExp.$1,
RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
);
}
}
return format;
}

function getYearMonthDay(date) {
return format(date || new Date(), "yyyyMMdd");
}

function getYearMonth(date) {
return format(date || new Date(), "yyyyMM");
}

console.log(getYearMonthDay());
console.log(getYearMonth());
console.log(getYearMonthDay(new Date(1999, 1, 3)));
console.log(getYearMonth(new Date(1999, 1, 3)));

身份证验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
const isIDCard = function(str) {
// 验证是否身份证号
var city = {
11: "北京",
12: "天津",
13: "河北",
14: "山西",
15: "内蒙古",
21: "辽宁",
22: "吉林",
23: "黑龙江 ",
31: "上海",
32: "江苏",
33: "浙江",
34: "安徽",
35: "福建",
36: "江西",
37: "山东",
41: "河南",
42: "湖北 ",
43: "湖南",
44: "广东",
45: "广西",
46: "海南",
50: "重庆",
51: "四川",
52: "贵州",
53: "云南",
54: "西藏 ",
61: "陕西",
62: "甘肃",
63: "青海",
64: "宁夏",
65: "新疆",
71: "台湾",
81: "香港",
82: "澳门",
91: "国外"
};

// 基础验证,长度验证
if (!str || !/^\d{17}(\d|x)$/i.test(str)) {
return false;
}

// 验证地址编码
if (!city[parseInt(str.substr(0, 2))]) {
return false;
}

// 验证出生日期
var birthdayStr =
str.substr(6, 4) + "/" + str.substr(10, 2) + "/" + str.substr(12, 2);
var birthday = new Date(birthdayStr);
if (!birthday) return false;
var transBirthdayStr =
birthday.getFullYear() +
"/" +
(birthday.getMonth() >= 9
? birthday.getMonth() + 1
: "0" + (birthday.getMonth() + 1)) +
"/" +
(birthday.getDate() >= 10 ? birthday.getDate() : "0" + birthday.getDate());
if (birthdayStr != transBirthdayStr) {
// 日期有误
return false;
}

// 验证长度和校验位
if (str.length == 18) {
str = str.split("");
//∑(ai×Wi)(mod 11)
//加权因子
var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
//校验位
var parity = [1, 0, "X", 9, 8, 7, 6, 5, 4, 3, 2];
var sum = 0;
var ai = 0;
var wi = 0;
for (var i = 0; i < 17; i++) {
ai = str[i];
wi = factor[i];
sum += ai * wi;
}
var last = parity[sum % 11];
if (parity[sum % 11] != str[17].toUpperCase()) {
return false;
}
}

return true;
};

设定参数必须传递

1
2
3
4
5
6
7
8
9
10
function throwIfMissing() {
throw new Error("Missing parameter");
}

function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}

foo();
// Error: Missing parameter

为浮点数运算部署一个误差检查函数

1
2
3
4
5
6
7
8
9
function withinErrorMargin(left, right) {
return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}

0.1 + 0.2 === 0.3; // false
withinErrorMargin(0.1 + 0.2, 0.3); // true

1.1 + 1.3 === 2.4; // false
withinErrorMargin(1.1 + 1.3, 2.4); // true

正确返回字符串长度的函数(Unicode 字符)

1
2
3
4
5
6
7
8
9
function codePointLength(text) {
var result = text.match(/[\s\S]/gu);
return result ? result.length : 0;
}

var s = "𠮷𠮷";

s.length; // 4
codePointLength(s); // 2

apply & call

1
2
3
4
5
6
7
8
9
var a = { x: 1 };
var b = { x: 2 };
function f() {
console.log(this.x);
}
a.f = f;
b.f = f;
f.apply(a); //1
f.apply(b); //2

返回对象的类

1
2
3
4
// p214
function classof(o) {
return Object.prototype.toString.call(o).slice(8, -1);
}

输出对象的类:Object.prototype.toString.call

1
2
3
4
5
Object.prototype.toString.call(1);
("[object Number]");

Object.prototype.toString.call(function() {});
("[object Function]");

typeof 得到的是类型;

How to pass url query params

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function getUrl(url, options) {
if (!isEmptyObject(options)) {
url += (url.indexOf("?") === -1 ? "?" : "&") + queryParams(options);
}
return url;
}

function queryParams(params) {
return Object.keys(params)
.map(k => encodeURIComponent(k) + "=" + encodeURIComponent(params[k]))
.join("&");
}

// 判断对象是否为空 https://stackoverflow.com/questions/4994201/is-object-empty
function isEmptyObject(obj) {
if (obj == null) return true;
if (obj.length > 0) return false;
if (obj.length === 0) return true;
if (typeof obj != "object") return true;

for (var name in obj) {
if (obj.hasOwnProperty(name)) {
return false;
}
}
return true;
}

var url = getUrl("http://lyloou.com", {});
console.log(url);

对象转数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
json2array = function(json) {
let result = [];
let keys = Object.keys(json);
keys.forEach(function(key) {
result.push(json[key]);
});
return result;
};

arr2Obj = function(array) {
var obj = new Object();
if (typeof array == "object") {
for (var i in array) {
var thisEle = convArrToObj(array[i]);
obj[i] = thisEle;
}
} else {
obj = array;
}
return obj;
};

输入银行卡号,4 位自动加上空格分隔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>test</title>
<script type="text/javascript">
window.onload = function() {
document.getElementById("test").onkeyup = function() {
this.value = this.value
.replace(/\s/g, "")
.replace(/\D/g, "")
.replace(/(\d{4})(?=\d)/g, "$1 ");
};
};
</script>
</head>
<body>
<input type="text" id="test" />
</body>
</html>

在前台读取数字时自动忽略空格

1
2
// 这样的话会把a b c d读取为abc
String newStr = "a b c".replaceAll(" ","");

正则摘取文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// var input = "'Warehouse','Local Release','Local Release DA'";
var input =
'【丝路亚心】250gX4<font color="red">核桃仁</font> 原味生<font color="red">核桃核桃仁</font> 新疆特产, <font color="red">123445</font>bcdef';
var regex = /<font[^>]+?>(.*?)<\/font>/g;

var matches,
output = [];
while ((matches = regex.exec(input))) {
output.push(matches[1]);
}
console.log(matches);
console.log(output);

function replaceText(text) {
return text.replace(/<font[^>]*>/g, "").replace(/<\/font>/g, "");
}

var newStr = replaceText(input);
console.log(newStr);

var arr = [];
for (let i in output) {
let val = output[i];
console.log(val);
let index = newStr.indexOf(val);
arr.push({ type: "normal", value: newStr.substring(0, index) });
arr.push({ type: "highlight", value: val });
newStr = newStr.substring(index + val.length);
}

if (newStr) {
arr.push({ type: "normal", value: newStr });
}

console.log(arr);
// [{"type":"normal","value":"【丝路亚心】250gX4"},{"type":"highlight","value":"核桃仁"},{"type":"normal","value":" 原味生"},{"type":"highlight","value":"核桃核桃仁"},{"type":"normal","value":" 新疆特产, "},{"type":"highlight","value":"123445"},{"type":"normal","value":"bcdef"}]

官方文档:https://cn.vuejs.org

Uploading Files With VueJS and Axios

读懂原理:

视频教程:vue.js入门教程

注意事项

  • webpack结合vue的时候,less中的元素重名会导致渲染错误。可以通过添加前缀来区别:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    .a{
    .container {
    background:#fff;
    }

    .nihao {
    color:#2f0;
    background:#c38;
    }
    }

    .b{
    .container {
    background:#000;
    }

    .nihao {
    color: #333;
    }
    }

通过scoped来避免全局污染

1
2
3
4
<style lang="less" scoped>
@import "../../styles/product-list.less";
</style>

Vue 固定滚动位置的处理办法. - 前端 - 掘金

1
2
3
4
5
6
7
8
9
# 使用vue-webpack模板
vue init webpack my-webpack-demo # webpack是项目模板,my-webpack-demo是项目名称。

cd my-webpack-demo
npm install

npm run dev

npm run build # 发布

设置状态栏透明,并保持住 toolbar 在status之下(支持 API 19 以上):

  1. 添加到 theme: <item name="android:windowTranslucentStatus">true</item>
  2. 使 toolbar 的 marginTop 正好等于 statusHeight:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public static void setToolbarMarginTop(Activity activity, Toolbar toolbar){
    ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) toolbar.getLayoutParams();
    params.topMargin = getStatusBarHeight(activity);
    }

    public static int getStatusBarHeight(Activity activity){
    int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
    int statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId);
    return statusBarHeight;
    }

tab悬浮效果实现

一种解决方式是使用开源库(如果布局嵌套很复杂,则不一定适合):https://github.com/carlonzo/StikkyHeader

另一种解决思路:

  • 在布局中,使用一个同tab1完全一样的tab2(并设置显示属性为gone);

  • 获取 tab1 到外层父视图顶部的距离(通过tab1.getTop()获取);

  • 当滚动的距离超过这个距离时,让外层的tab2显示出来,否则隐藏tab2;(即:(scrollY - tab1.getTop()) > 0

  • tab1 和 tab2 在逻辑上做相同的变化;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <FrameLayout>
    <ScrollTabView>
    <LinearLayout>
    <...>
    <tab1>
    <...>
    </LinearLayout>
    </ScrollTabView>

    <tab2 visibility="gone">
    </FrameLayout>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    // 重写ScrollView
    // 参考资料:http://www.jianshu.com/p/8ee4b0896b22
    // https://github.com/aohanyao/Advanced/blob/master/code/CustomView/ScollTabView/app/src/main/java/aohanyao/com/scolltabview/ScrollLevitateTabView.java
    public class ScrollTabView extends ScrollView {
    OnScrollListener mOnScrollListener;

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

    public ScrollTabView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    }

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

    public void setOnScrollListener(OnScrollListener onScrollListener) {
    mOnScrollListener = onScrollListener;
    }

    private void setUp() {
    if (mOnScrollListener == null) {
    return;
    }

    getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
    mOnScrollListener.onScroll(getScrollY());
    }
    });
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    if (mOnScrollListener != null) {
    mOnScrollListener.onScroll(t);
    }
    super.onScrollChanged(l, t, oldl, oldt);
    }

    public interface OnScrollListener {
    void onScroll(int scrollY);
    }
    }

    // 运用
    ScrollTabView scrollTabView = (ScrollTabView) mView.findViewById(R.id.scroll_tab_view);
    scrollTabView.setOnScrollListener(new ScrollTabView.OnScrollListener() {
    @Override
    public void onScroll(int scrollY) {
    // ScrollView的高度变化决定了tab2的显示与否
    int deltaY = scrollY - tab1.getTop();
    tab2.setVisibility(deltaY > 0 ? View.VISIBLE : View.GONE);
    }
    });

RecyclerView

  • 针对多种类型的情况,可以创建多个 ViewHolder和设置多个 type

分页加载(加载更多)

https://github.com/lyloou/lou/blob/demo/test/src/main/java/com/lyloou/test/gank/GankWelfareActivity.java

http://www.gadgetsaint.com/android/recyclerview-header-footer-pagination/#.WRwxJGh96Hs

方案一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);

int lastvisibleitemposition = mLayoutManager.findLastVisibleItemPosition();

if (lastvisibleitemposition == mAdapter.getItemCount() - 1) {

if (!loading && !isLastPage) {

loading = true;
fetchData((++pageCount));
// Increment the pagecount everytime we scroll to fetch data from the next page
// make loading = false once the data is loaded
// call mAdapter.notifyDataSetChanged() to refresh the Adapter and Layout

}


}
}
});

方案二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// http://www.jianshu.com/p/4feb0c16d1b5
private RecyclerView.OnScrollListener mListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);

LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int lastVisibleItem = layoutManager.findLastVisibleItemPosition();
int totalItemCount = layoutManager.getItemCount();

if (totalItemCount < 250 && lastVisibleItem >= totalItemCount - 4) {
// 注意:要限制请求,否则请求太多次数,导致服务器崩溃或者服务器拒绝请求(罪过,罪过)。
if (mIsLoading) {
Log.i(TAG, "onScrolled: " + "加载中---------");
} else {
Log.i(TAG, "onScrolled: " + "加载更多了=======》");
loadSubject();
}

}

Log.d(TAG, "onScrolled: lastVisibleItem=" + lastVisibleItem);
Log.d(TAG, "onScrolled: totalItemCount=" + totalItemCount);

}
};

加载更多调用 notifyDataSetChanged 时会白一下屏

1
2
3
4
5
6
getDataListFromNet(++page, dataList->{
int lastSize = mAdapter.getList().size();
mAdapter.getList().addAll(dataList);
// mAdapter.notifyDataSetChanged(); // 调用这个方法会白屏,可以用下面的方式解决
mAdapter.notifyItemRangeInserted(lastSize - 1, dataList.size());
});

GridLayoutManager 均分两列的装饰器 ItemDecoration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class DoubleItemOffsetDecoration extends RecyclerView.ItemDecoration {
private int offset;

public DoubleItemOffsetDecoration(int offset) {
this.offset = offset;
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildLayoutPosition(view);
if (position == 0 || position == 1) {
outRect.top = offset;
}

outRect.bottom = offset;
if (position % 2 == 1) {
outRect.left = offset / 2;
outRect.right = offset;
} else {
outRect.left = offset;
outRect.right = offset / 2;
}
}
}

根据图片的文件路径,显示给ImageView

原理:借助 Glide 库来实现;

1
2
3
String pathname = "/storage/emulated/0/lyloou/image/hello_world.png";
File file = new File(pathname);
Glide.with(mContext).load(file).asBitmap().into(mIvHi);

通过图像 URL 设置圆角图像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void setCircleIcon(ImageView view) {
String url = "https://img.gcall.com/dca5/M00/10/8E/wKhoNlggetaENWylAAAAAAAAAAA457.jpg";
final int w = Uscreen.dp2Px(mContext, 48);
Glide.with(mContext)
.load(url)
.centerCrop()
.fitCenter()
.thumbnail(0.1f)
.placeholder(R.mipmap.ic_launcher)
.crossFade()
.override(w, w)
.transform(new BitmapTransformation(mContext) {
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return Uview.getBitmapByXfermode(mContext, toTransform,
Color.parseColor("#dddddd"),
w,
w, PorterDuff.Mode.SRC_IN);
}

@Override
public String getId() {
return getClass().getName();
}
})
.into(view);
}
0%