使用

1
mContext.startActivity(new Intent(mContext, CropImageActivity.class));

权限声明

1
2
3
4
5
6
7
8
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />

注册Activity

1
2
3
4
<activity
android:name=".CropImageActivity"
android:screenOrientation="portrait"
android:theme="@style/myTransparent" />

注册Provider

1
2
3
4
5
6
7
8
9
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${PACKAGE_NAME}.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>

透明的theme

1
2
3
4
5
6
7
8

<style name="myTransparent" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
<item name="android:windowBackground">@color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
</style>

xml/file_paths

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
<!-- res/xml/file_paths -->

<?xml version="1.0" encoding="utf-8"?>
<paths>
<root-path
name="root"
path="" />

<files-path
name="files"
path="" />

<cache-path
name="cache"
path="" />

<external-path
name="external"
path="" />

<external-files-path
name="external_file"
path="" />

<external-cache-path
name="external_cache"
path="" />
</paths>

util/uri

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

/**
* 类:SystemProgramUtils 系统程序适配
* 1. 拍照
* 2. 相册
* 3. 裁切
* 作者: qxc
* 日期:2018/2/23.
*/
public class SystemProgramUtils {
public static final int REQUEST_CODE_PAIZHAO = 1;
public static final int REQUEST_CODE_ZHAOPIAN = 2;
public static final int REQUEST_CODE_CAIQIE = 3;

public static void paizhao(Activity activity, File outputFile) {
Intent intent = new Intent();
intent.setAction("android.media.action.IMAGE_CAPTURE");
intent.addCategory("android.intent.category.DEFAULT");
Uri uri = FileProviderUtils.uriFromFile(activity, outputFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
activity.startActivityForResult(intent, REQUEST_CODE_PAIZHAO);
}

public static void zhaopian(Activity activity) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction("android.intent.action.PICK");
intent.addCategory("android.intent.category.DEFAULT");
activity.startActivityForResult(intent, REQUEST_CODE_ZHAOPIAN);
}

public static void Caiqie(Activity activity, Uri uri, File outputFile, int width, int height) {
Intent intent = new Intent("com.android.camera.action.CROP");
FileProviderUtils.setIntentDataAndType(activity, intent, "image/*", uri, true);
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", width);
intent.putExtra("outputY", height);
//return-data为true时,直接返回bitmap,可能会很占内存,不建议,小米等个别机型会出异常!!!
//所以适配小米等个别机型,裁切后的图片,不能直接使用data返回,应使用uri指向
//裁切后保存的URI,不属于我们向外共享的,所以可以使用fill://类型的URI
Uri outputUri = Uri.fromFile(outputFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
intent.putExtra("return-data", false);

intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true);
activity.startActivityForResult(intent, REQUEST_CODE_CAIQIE);
}
}


/**
* 类:FileProviderUtils
* 从APP向外共享的文件URI时,必须使用该类进行适配,否则在7.0以上系统,会报错:FileUriExposedException(文件Uri暴露异常)
* 作者: qxc
* 日期:2018/2/23.
*/
public class FileProviderUtils {
/**
* 从文件获得URI
*
* @param activity 上下文
* @param file 文件
* @return 文件对应的URI
*/
public static Uri uriFromFile(Activity activity, File file) {
Uri fileUri;
//7.0以上进行适配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
String p = activity.getPackageName() + ".FileProvider";
fileUri = FileProvider.getUriForFile(
activity,
p,
file);
} else {
fileUri = Uri.fromFile(file);
}
return fileUri;
}

/**
* 设置Intent的data和类型,并赋予目标程序临时的URI读写权限
*
* @param activity 上下文
* @param intent 意图
* @param type 类型
* @param file 文件
* @param writeAble 是否赋予可写URI的权限
*/
public static void setIntentDataAndType(Activity activity,
Intent intent,
String type,
File file,
boolean writeAble) {
//7.0以上进行适配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setDataAndType(uriFromFile(activity, file), type);
//临时赋予读写Uri的权限
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (writeAble) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
} else {
intent.setDataAndType(Uri.fromFile(file), type);
}
}

/**
* 设置Intent的data和类型,并赋予目标程序临时的URI读写权限
*
* @param context 上下文
* @param intent 意图
* @param type 类型
* @param fileUri 文件uri
* @param writeAble 是否赋予可写URI的权限
*/
public static void setIntentDataAndType(Context context,
Intent intent,
String type,
Uri fileUri,
boolean writeAble) {
//7.0以上进行适配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setDataAndType(fileUri, type);
//临时赋予读写Uri的权限
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (writeAble) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
} else {
intent.setDataAndType(fileUri, type);
}
}
}

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

// [Android 拍照,从图库选择照片,并裁剪,上传到服务器 - 简书](https://www.jianshu.com/p/bfd9fe0592cb)
// [拍照/从相册读取图片后进行裁剪的方法 - developer_Kale - 博客园](http://www.cnblogs.com/tianzhijiexian/p/3989296.html)
// [Android 7.0适配 -- FileProvider 拍照、选择相册、裁切图片, 小米机型适配 - 简书](https://www.jianshu.com/p/bec4497c2a63)

public class CropImageActivity extends Activity {
private static final String TAG = CropImageActivity.class.getSimpleName();
public static final String TMP_DIR = Environment.getExternalStorageDirectory() + "/renrenyoupin/tmp";
private int width;
private int height;
private File file;
private Activity mContext;
private static final int CAMERA_REQUEST_CODE = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;

if (savedInstanceState == null) {
initIconFile(true);

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
showDialog();
return;
}
requestPermission();
}
}

private void initIconFile(boolean remove) {
File tmpDir = new File(TMP_DIR);
if (!tmpDir.exists()) {
tmpDir.mkdirs();
}
file = new File(tmpDir.getAbsolutePath(), "icon.png");
if (remove && file.exists()) {
file.delete();
}
}


@TargetApi(Build.VERSION_CODES.M)
private void requestPermission() {
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
showDialog();
} else {
//申请相机权限和STORAGE权限
ActivityCompat.requestPermissions(
mContext,
new String[]{Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
},
CAMERA_REQUEST_CODE);
}
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == CAMERA_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED
&& grantResults[2] == PackageManager.PERMISSION_GRANTED) {
showDialog();
} else {
String msg = "没有相关运行权限";
//Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
backWithError(msg);
}
}
}

private void showDialog() {
Intent intent = getIntent();
width = intent.getIntExtra("width", 0);
height = intent.getIntExtra("height", 0);
if (width == 0) {
width = 350;
}
if (height == 0) {
height = 350;
}

new AlertDialog.Builder(this)
.setTitle("更换头像")
.setItems(new String[]{"拍照", "从相册选择",}, (dialog, which) -> {
switch (which) {
case 0://拍照
SystemProgramUtils.paizhao(mContext, file);
break;
case 1://从相册选择
SystemProgramUtils.zhaopian(mContext);
break;
}
})
.setOnCancelListener(dialog -> finish())
.create()
.show();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != SystemProgramUtils.REQUEST_CODE_PAIZHAO && data == null) {
finish();
return;
}

Bitmap bitmap;
Uri uri;
switch (requestCode) {
case SystemProgramUtils.REQUEST_CODE_PAIZHAO: // 从相机跳回来
if (resultCode == Activity.RESULT_CANCELED) {
finish();
return;
}

if (file == null) {
initIconFile(false);
}
if (!file.exists()) {
backWithError("拍照异常");
return;
}
// 启动裁剪器
SystemProgramUtils.Caiqie(mContext, FileProviderUtils.uriFromFile(mContext, file), file, width, height);
break;
case SystemProgramUtils.REQUEST_CODE_ZHAOPIAN: // 从图库跳回来
// 此处的uri 是content类型的。 还有一种是file 型的。应该转换为后者
uri = data.getData();
if (uri == null) {
backWithError("图片无效");
return;
}
SystemProgramUtils.Caiqie(mContext, uri, file, width, height);
break;
case SystemProgramUtils.REQUEST_CODE_CAIQIE: // 从裁剪处跳回来
try {
uri = Uri.fromFile(file);
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
// 方法1
uri = saveBitmap(bitmap);
if (uri == null) {
backWithError("裁剪失败");
return;
}
sentImage(uri.getPath());
} catch (Exception ex) {
ex.printStackTrace();
}


break;
}
}

private void sentImage(String path) {
UokHttp.init().postImageFile(new ICallBack() {
@Override
public String getUrl() {
return APP_URL + "/android/user_center/person/edit_avatar";
}

@Override
public void doResponse(JSONObject responseInfo) {
backWithSuccess(responseInfo);
}

@Override
public void doError(String errorMsg) {
backWithError(errorMsg);
}
}, path);
}

private void backWithSuccess(JSONObject responseInfo) {
if (Rryp.instance != null && Rryp.instance.getCurrentCallbackContext() != null) {
Rryp.instance.getCurrentCallbackContext().success(responseInfo);
}
finish();
}

private void backWithError(String errorMsg) {
if (Rryp.instance != null && Rryp.instance.getCurrentCallbackContext() != null) {
Rryp.instance.getCurrentCallbackContext().error(errorMsg);
}
finish();
}

/**
* 把 Bitmap 保存在SD卡路径后,返回file 类型的 uri
*
* @param bm
* @return
*/
private Uri saveBitmap(Bitmap bm) {
if (bm == null) {
return null;
}

try {
FileOutputStream fos = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.PNG, 80, fos);
fos.flush();
fos.close();
return Uri.fromFile(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}

}

参考资料

https://www.jooq.org/doc/3.9/manual/#Overview

C

R

U

D

插入或更新

如果没有就插入,如果有则更新

1
2
3
4
5
6
7
8
9
// 工具方法 https://github.com/jOOQ/jOOQ/issues/2134
import org.jooq.Field;
import org.jooq.impl.DSL;

public class MyDSL {
public static <T> Field<T> values(Field<T> field) {
return DSL.field("VALUES({0})", field.getDataType(), field);
}
}
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
private void insertCouponItemsToDb(List<TbkDgItemCouponGetResponse.TbkCoupon> results) {

dslContext.transaction((configuration) -> {

InsertValuesStep19<TbkGoodsRecord, Long, Integer,
String, Timestamp, Timestamp,
String, Integer, Integer,
String, String, Long,
String, String, String,
Byte, Integer, BigDecimal,
String, BigDecimal> step =
DSL.using(configuration).insertInto(TBK_GOODS,
TBK_GOODS.NUM_IID,
TBK_GOODS.CATEGORY,
TBK_GOODS.COUPON_CLICK_URL,
TBK_GOODS.COUPON_START_TIME,
TBK_GOODS.COUPON_END_TIME,
TBK_GOODS.COUPON_INFO,
TBK_GOODS.COUPON_REMAIN_COUNT,
TBK_GOODS.COUPON_TOTAL_COUNT,
TBK_GOODS.NICK,
TBK_GOODS.PICT_URL,
TBK_GOODS.SELLER_ID,
TBK_GOODS.SHOP_TITLE,
TBK_GOODS.SMALL_IMAGES,
TBK_GOODS.TITLE,
TBK_GOODS.USER_TYPE,
TBK_GOODS.VOLUME,
TBK_GOODS.ZK_FINAL_PRICE,
TBK_GOODS.ITEM_DESCRIPTION,
TBK_GOODS.TK_RATE
);

results.forEach(tbkCoupon -> {
step.values(tbkCoupon.getNumIid(),
tbkCoupon.getCategory().intValue(),
tbkCoupon.getCouponClickUrl(),
formatTime(tbkCoupon.getCouponStartTime()),
formatTime(tbkCoupon.getCouponEndTime()),
tbkCoupon.getCouponInfo(),
tbkCoupon.getCouponRemainCount().intValue(),
tbkCoupon.getCouponTotalCount().intValue(),
tbkCoupon.getNick(),
tbkCoupon.getPictUrl(),
tbkCoupon.getSellerId(),
tbkCoupon.getShopTitle(),
StringUtil.convertListToArrayStringWithDoubleQuotation(tbkCoupon.getSmallImages()),
tbkCoupon.getTitle(),
tbkCoupon.getUserType().byteValue(),
tbkCoupon.getVolume().intValue(),
BigDecimal.valueOf(Double.parseDouble(tbkCoupon.getZkFinalPrice())),
tbkCoupon.getItemDescription(),
BigDecimal.valueOf(Double.parseDouble(tbkCoupon.getCommissionRate()))
);
});

step.onDuplicateKeyUpdate()
.set(TBK_GOODS.CATEGORY, MyDSL.values(TBK_GOODS.CATEGORY))
.set(TBK_GOODS.COUPON_CLICK_URL, MyDSL.values(TBK_GOODS.COUPON_CLICK_URL))
.set(TBK_GOODS.COUPON_START_TIME, MyDSL.values(TBK_GOODS.COUPON_START_TIME))
.set(TBK_GOODS.COUPON_END_TIME, MyDSL.values(TBK_GOODS.COUPON_END_TIME))
.set(TBK_GOODS.COUPON_INFO, MyDSL.values(TBK_GOODS.COUPON_INFO))
.set(TBK_GOODS.COUPON_REMAIN_COUNT, MyDSL.values(TBK_GOODS.COUPON_REMAIN_COUNT))
.set(TBK_GOODS.NICK, MyDSL.values(TBK_GOODS.NICK))
.set(TBK_GOODS.PICT_URL, MyDSL.values(TBK_GOODS.PICT_URL))
.set(TBK_GOODS.SELLER_ID, MyDSL.values(TBK_GOODS.SELLER_ID))
.set(TBK_GOODS.SHOP_TITLE, MyDSL.values(TBK_GOODS.SHOP_TITLE))
.set(TBK_GOODS.SMALL_IMAGES, MyDSL.values(TBK_GOODS.SMALL_IMAGES))
.set(TBK_GOODS.TITLE, MyDSL.values(TBK_GOODS.TITLE))
.set(TBK_GOODS.USER_TYPE, MyDSL.values(TBK_GOODS.USER_TYPE))
.set(TBK_GOODS.VOLUME, MyDSL.values(TBK_GOODS.VOLUME))
.set(TBK_GOODS.ZK_FINAL_PRICE, MyDSL.values(TBK_GOODS.ZK_FINAL_PRICE))
.set(TBK_GOODS.ITEM_DESCRIPTION, MyDSL.values(TBK_GOODS.ITEM_DESCRIPTION))
.set(TBK_GOODS.TK_RATE, MyDSL.values(TBK_GOODS.TK_RATE))
.executeAsync(executorService);

});

}


private Timestamp formatTime(String time) {

if (Strings.isNullOrEmpty(time)) {
return null;
}
return Timestamp.valueOf(time + " 00:00:00");
}

判断某个字段是否为空

1
step.and(TbkGoods.TBK_GOODS.COUPON_INFO.length().notEqual(0));

启动app的时候闪黑屏?

可以用自己app的logo来代替黑屏,且看如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- res/drawable/welcome_bg -->
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="@color/colorPrimary" />
</item>
<item>
<bitmap
android:gravity="center"
android:src="@mipmap/screen"
android:tileMode="disabled" />
</item>
</layer-list>
1
2
3
4
<!-- add below code to your style.xml -->
<style name="WelcomeAppTheme" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
<item name="android:windowBackground">@drawable/welcome_bg</item>
</style>

预加载主内容

为了在展示欢迎页的时候提前加载,我们调换下MainActivity和WelcomeActivity顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- add below code to your AndroidMainfest.xml -->
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/AppTheme">
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".WelcomeActivity"
android:exported="true"
android:screenOrientation="portrait"
android:theme="@style/WelcomeAppTheme" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
toNext();

initData();
initView();
}
private void toNext() {
if (MyApplication.sSkipWelcome) {
return;
}
MyApplication.sSkipWelcome = true;

Intent intent = new Intent(this, WelcomeActivity.class);
startActivity(intent);
overridePendingTransition(0, 0); // 禁用动画
}

可以参考: https://github.com/lyloou/lou/blob/demo/test/src/main/AndroidManifest.xml

启动已经存在的activity

  • android - Resume the Activity instead of Starting if already exists in back stack - Stack Overflow
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    You can add this two lines and try:

    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    Write this in your manifest file inside Activity

    <activity
    android:name=".SettingsActivity"
    android:launchMode="singleInstance"
    android:screenOrientation="portrait" >
    </activity>

    "singleTask" and "singleInstance" activities can only begin a task. They are always at the root of the activity stack. Moreover, the device can hold only one instance of the activity at a time — only one such task.

    You can use SingleTask or SingleInstance

    "singleTask" - The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one.

    "singleInstance" - Same as "singleTask", except that the system doesn't launch any other activities into the task holding the instance. The activity is always the single and only member of its task.

    Refer this link http://developer.android.com/guide/topics/manifest/activity-element.html

可以参考: https://github.com/lyloou/lou/blob/demo/test/src/main/java/com/lyloou/test/WelcomeActivity.java

How to change visudo editor from nano to vim?

sudo update-alternatives --config editor

Vim-Plug

https://github.com/junegunn/vim-plug

1
2
curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

用 vim 编辑上一次输入的命令

1
fc

当你在 vi 中修改了半天配置文件,然后发现没有写权限,没有比这更令人感到挫败了,此时你需要这条命令。

1
2
3
4
5
6
:w !sudo tee %
# or
:w ! sudo tee %

# occur error
:w! sudo tee %

Alternative tab navigation | Vim Tips Wiki | FANDOM powered by Wikia

1
2
3
4
5
6
7
8
9
10
11
12
13
14
" Tab navigation like Firefox.
nnoremap <C-S-tab> :tabprevious<CR>
nnoremap <C-tab> :tabnext<CR>
inoremap <C-S-tab> <Esc>:tabprevious<CR>i
inoremap <C-tab> <Esc>:tabnext<CR>i

nnoremap <C-PageDown> :tabprevious<CR>
nnoremap <C-PageUp> :tabnext<CR>
inoremap <C-PageDown> <Esc>:tabprevious<CR>i
inoremap <C-PageUp> <Esc>:tabnext<CR>i

nnoremap <C-t> :tabnew<CR>
inoremap <C-t> <Esc>:tabnew<CR>

vim 插件: delimitmate[符号自动补全]

触发后, 假设你要跳到补全后的符号后面继续编辑, 按 Shift-Tab

You don’t need more than one cursor in vim

vim 插件: easy-align[快速对齐]

[分享]关于 vim

vim 文本行逆序化_my_live_123-CSDN 博客_vim 倒序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
要求

示例:将文本
1234
123
12
1
转换成
1
12
123
1234
命令

:g/.*/mo0
或者
:g/^/mo0

补充说明
原理是从第一行开始,匹配每一行,然后执行mo(move)操作移动到第0行。如此处理每一行,直到文本末行。执行完毕文本逆序化成功!
> https://blog.csdn.net/cwcmcw/article/details/44876267

修改 hostname

1
2
vi /etc/hostname
reboot

Linux Basics - Set a Static IP on Ubuntu

Here is an example for an older Ubuntu Release:

1
2
3
4
5
6
auto lo eth0
iface lo inet loopback
iface eth0 inet static
address 192.168.1.100
netmask 255.255.255.0
gateway 192.168.1.1

And here an example for Ubuntu 16.04 and newer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# test

# The primary network interface
auto ens33
iface ens33 inet static
address 192.168.1.100
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1
dns-nameservers 8.8.8.8 8.8.4.4

Changing ubuntu server’s language to english

1
sudo apt-get install language-pack-en language-pack-en-base manpages
1
2
# /etc/default/locale
LANG="en_US.UTF-8"
1
2
## OR
sudo update-locale LANG=en_US.UTF-8

build-essential

1
apt-get install build-essential

or

1
2
sudo apt-get install aptitude
sudo aptitude install build-essential

switch between gdm,lightdm

http://www.webupd8.org/2011/07/how-to-switch-between-gdm-lightdm-or.html

1
sudo dpkg-reconfigure lightdm

[How can I define startup applications with the Awesome Window Manager? - Ask Ubuntu](

https://askubuntu.com/questions/57264/how-can-i-define-startup-applications-with-the-awesome-window-manager)

Starting from a template
First you’ll need to copy the template rc.lua file into your home folder

1
2
mkdir ~/.config/awesome
cp /etc/xdg/awesome/rc.lua ~/.config/awesome/

Defining applications to start
Now using awesome - edit config copy the following code at the bottom of your new rc.lua file

1
2
3
4
5
6
7
8
9
10
11
do
local cmds =
{
"firefox",
"xedit"
}

for _,i in pairs(cmds) do
awful.util.spawn(i)
end
end

In this example - firefox and xedit are run on startup.
An excellent wiki page describing this and much more can be found on ArchLinux

rootlogin

root 账号不能登录,提示 ssh permission denied 问题

1
2
3
4
5
vi /etc/ssh/sshd_config
# add below line
PermitRootLogin yes
# then restart sshd
service sshd restart

ubuntu 设置静态网络

  1. 修改 /etc/netplan/00-installer-config.yaml
1
2
3
4
5
6
7
8
9
10
11
network:
version: 2
# renderer: networkd
ethernets:
enp0s3:
dhcp4: no
addresses: [192.168.137.15/24]
gateway4: 192.168.137.1
nameservers:
# search: [itzgeek.local]
addresses: [233.5.5.5, 233.6.6.6]
  1. 执行命令:netplay apply 即可生效

install on Ubuntu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Steps to build and install tmux from source on Ubuntu.
# Takes < 25 seconds on EC2 env [even on a low-end config instance].
VERSION=2.8
sudo apt-get -y remove tmux
sudo apt-get -y install wget tar libevent-dev libncurses-dev
wget https://github.com/tmux/tmux/releases/download/${VERSION}/tmux-${VERSION}.tar.gz
tar xf tmux-${VERSION}.tar.gz
rm -f tmux-${VERSION}.tar.gz
cd tmux-${VERSION}
./configure
make
sudo make install
cd -
sudo rm -rf /usr/local/src/tmux-*
sudo mv tmux-${VERSION} /usr/local/src

## Logout and login to the shell again and run.
tmux -V

install on centos

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
# [Install tmux 2.8 on centos 7](https://gist.github.com/pokev25/4b9516d32f4021d945a140df09bf1fde)
# Install tmux 2.8 on Centos
VERSION=2.8

# install deps
yum install gcc kernel-devel make ncurses-devel

# DOWNLOAD SOURCES FOR LIBEVENT AND MAKE AND INSTALL
curl -LOk https://github.com/libevent/libevent/releases/download/release-2.1.8-stable/libevent-2.1.8-stable.tar.gz
tar -xf libevent-2.1.8-stable.tar.gz
cd libevent-2.1.8-stable
./configure --prefix=/usr/local
make
make install

# DOWNLOAD SOURCES FOR TMUX AND MAKE AND INSTALL

curl -LOk https://github.com/tmux/tmux/releases/download/${VERSION}/tmux-${VERSION}.tar.gz
tar -xf tmux-${VERSION}.tar.gz
rm -f tmux-${VERSION}.tar.gz
cd tmux-${VERSION}
LDFLAGS="-L/usr/local/lib -Wl,-rpath=/usr/local/lib" ./configure --prefix=/usr/local
make
make install
cd -
sudo rm -rf /usr/local/src/tmux-*
sudo mv tmux-${VERSION} /usr/local/src

# pkill tmux
# close your terminal window (flushes cached tmux executable)
# open new shell and check tmux version
tmux -V

Tmux 快捷键 & 速查表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bind r source-file ~/.tmux.conf \; display-message "Config reloaded"

bind '"' split-window -c '#{pane_current_path}'
bind '%' split-window -h -c '#{pane_current_path}'

bind Escape copy-mode
bind-key -Tcopy-mode-vi 'v' send -X begin-selection
bind-key -Tcopy-mode-vi 'y' send -X copy-selection
unbind p
bind p pasteb
setw -g mode-keys vi # Vi风格选择文本
# set-window-option -g mode-keys vi

# https://stackoverflow.com/questions/17445100/getting-back-old-copy-paste-behaviour-in-tmux-with-mouse
set -g mouse off # 不启用鼠标(启用鼠标会导致无法复制到系统)

########### myself ##########
# select panel
bind k selectp -U
bind j selectp -D
bind h selectp -L
bind l selectp -R

set-option -g default-shell /bin/zsh

zip

1
2
3
4
5
6
7
https://unix.stackexchange.com/questions/46969/compress-a-folder-with-tar
To tar and gzip a folder, the syntax is:
tar czf name_of_archive_file.tar.gz name_of_directory_to_tar

tar -zcvf xxx.tar.gz aaa bbb
tar -jcvf xxx.tar.bz2 aaa bbb
tar -Jcvf xxx.tar.xz aaa bbb

unzip

1
2
3
4
5
tar -xf xxx.tar.gz
tar -xf xxx.tar.bz2
tar -Jxf xxx.tar.xz

tar -xf xxx.tar.gz -C ./xxx
0%