第一次提交
This commit is contained in:
1
uikit/.gitignore
vendored
Normal file
1
uikit/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
45
uikit/build.gradle
Normal file
45
uikit/build.gradle
Normal file
@@ -0,0 +1,45 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
|
||||
viewBinding {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 28
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
//noinspection GradleCompatible
|
||||
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
|
||||
implementation group: 'com.squareup.picasso', name: 'picasso', version: '2.5.2'
|
||||
// https://mvnrepository.com/artifact/com.github.bumptech.glide/glide
|
||||
implementation group: 'com.github.bumptech.glide', name: 'glide', version: '4.9.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
||||
implementation group: 'com.facebook.fresco', name: 'fresco', version: '1.8.1'
|
||||
|
||||
implementation project(path: ':base')
|
||||
}
|
||||
21
uikit/proguard-rules.pro
vendored
Normal file
21
uikit/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.fengliyan.uikit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() throws Exception {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("com.caivideo.uikit.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
14
uikit/src/main/AndroidManifest.xml
Normal file
14
uikit/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.fengliyan.uikit" >
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER" />
|
||||
|
||||
<application android:allowBackup="true" android:label="@string/app_name"
|
||||
android:supportsRtl="true">
|
||||
<activity
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:name="com.fengliyan.uikit.photopicker.MultiImageSelectorActivity" />
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,213 @@
|
||||
package com.fengliyan.uikit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.method.DigitsKeyListener;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
|
||||
public class ContentWithSpaceEditText extends AppCompatEditText {
|
||||
|
||||
public static final int TYPE_PHONE = 0;
|
||||
public static final int TYPE_BANK_CARD = 1;
|
||||
public static final int TYPE_ID_CARD = 2;
|
||||
private int start, count,before;
|
||||
private int contentType;
|
||||
private int maxLength = 50;
|
||||
private String digits;
|
||||
|
||||
public ContentWithSpaceEditText(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ContentWithSpaceEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
parseAttributeSet(context, attrs);
|
||||
}
|
||||
|
||||
public ContentWithSpaceEditText(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
parseAttributeSet(context, attrs);
|
||||
}
|
||||
|
||||
private void parseAttributeSet(Context context, AttributeSet attrs) {
|
||||
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ContentWithSpaceEditText, 0, 0);
|
||||
contentType = ta.getInt(R.styleable.ContentWithSpaceEditText_input_type, 0);
|
||||
// 必须调用recycle
|
||||
ta.recycle();
|
||||
initType();
|
||||
setSingleLine();
|
||||
addTextChangedListener(watcher);
|
||||
}
|
||||
|
||||
private void initType(){
|
||||
if (contentType == TYPE_PHONE) {
|
||||
maxLength = 13;
|
||||
digits = "0123456789 ";
|
||||
setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
} else if (contentType == TYPE_BANK_CARD) {
|
||||
maxLength = 31;
|
||||
digits = "0123456789 ";
|
||||
setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
} else if (contentType == TYPE_ID_CARD) {
|
||||
maxLength = 21;
|
||||
digits = null;
|
||||
setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
}
|
||||
setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputType(int type) {
|
||||
if (contentType == TYPE_PHONE || contentType == TYPE_BANK_CARD) {
|
||||
type = InputType.TYPE_CLASS_NUMBER;
|
||||
}else if(contentType == TYPE_ID_CARD){
|
||||
type = InputType.TYPE_CLASS_TEXT;
|
||||
}
|
||||
super.setInputType(type);
|
||||
/* 非常重要:setKeyListener要在setInputType后面调用,否则无效。*/
|
||||
if(!TextUtils.isEmpty(digits)) {
|
||||
setKeyListener(DigitsKeyListener.getInstance(digits));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置内容的类型
|
||||
* @param contentType 类型
|
||||
*/
|
||||
public void setContentType(int contentType) {
|
||||
this.contentType = contentType;
|
||||
initType();
|
||||
}
|
||||
|
||||
private TextWatcher watcher = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
ContentWithSpaceEditText.this.start = start;
|
||||
ContentWithSpaceEditText.this.before = before;
|
||||
ContentWithSpaceEditText.this.count = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (s == null) {
|
||||
return;
|
||||
}
|
||||
//判断是否是在中间输入,需要重新计算
|
||||
boolean isMiddle = (start + count) < (s.length());
|
||||
//在末尾输入时,是否需要加入空格
|
||||
boolean isNeedSpace = false;
|
||||
if (!isMiddle && isSpace(s.length())) {
|
||||
isNeedSpace = true;
|
||||
}
|
||||
if (isMiddle || isNeedSpace || count > 1) {
|
||||
String newStr = s.toString();
|
||||
newStr = newStr.replace(" ", "");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int spaceCount = 0;
|
||||
for (int i = 0; i < newStr.length(); i++) {
|
||||
sb.append(newStr.substring(i, i+1));
|
||||
//如果当前输入的字符下一位为空格(i+1+1+spaceCount),因为i是从0开始计算的,所以一开始的时候需要先加1
|
||||
if(isSpace(i + 2 + spaceCount)){
|
||||
sb.append(" ");
|
||||
spaceCount += 1;
|
||||
}
|
||||
}
|
||||
removeTextChangedListener(watcher);
|
||||
s.replace(0, s.length(),sb);
|
||||
//如果是在末尾的话,或者加入的字符个数大于零的话(输入或者粘贴)
|
||||
if (!isMiddle || count > 1) {
|
||||
setSelection(s.length() <= maxLength ? s.length() : maxLength);
|
||||
} else {
|
||||
//如果是删除
|
||||
if (count == 0) {
|
||||
//如果删除时,光标停留在空格的前面,光标则要往前移一位
|
||||
if (isSpace(start - before + 1)) {
|
||||
setSelection((start - before) > 0 ? start - before : 0);
|
||||
} else {
|
||||
setSelection((start - before + 1) > s.length() ? s.length() : (start - before + 1));
|
||||
}
|
||||
}
|
||||
//如果是增加
|
||||
else {
|
||||
if (isSpace(start - before + count)) {
|
||||
setSelection((start + count - before + 1) < s.length() ? (start + count - before + 1) : s.length());
|
||||
} else {
|
||||
setSelection(start + count - before);
|
||||
}
|
||||
}
|
||||
}
|
||||
addTextChangedListener(watcher);
|
||||
}
|
||||
}
|
||||
};
|
||||
public String getTextWithoutSpace() {
|
||||
return super.getText().toString().replace(" ", "");
|
||||
}
|
||||
public boolean checkTextRight(){
|
||||
String text = getTextWithoutSpace();
|
||||
//这里做个简单的内容判断
|
||||
if (contentType == TYPE_PHONE) {
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
showShort(getContext(), "手机号不能为空,请输入正确的手机号");
|
||||
} else if (text.length() < 11) {
|
||||
showShort(getContext(), "手机号不足11位,请输入正确的手机号");
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else if (contentType == TYPE_BANK_CARD) {
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
showShort(getContext(), "银行卡号不能为空,请输入正确的银行卡号");
|
||||
} else if (text.length() < 14) {
|
||||
showShort(getContext(), "银行卡号位数不正确,请输入正确的银行卡号");
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else if (contentType == TYPE_ID_CARD) {
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
showShort(getContext(), "SFZ号不能为空,请输入正确的SFZ号");
|
||||
} else if (text.length() < 18) {
|
||||
showShort(getContext(), "SFZ号不正确,请输入正确的SFZ号");
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private void showShort(Context context,String msg){
|
||||
Toast.makeText(context.getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
private boolean isSpace(int length) {
|
||||
if (contentType == TYPE_PHONE) {
|
||||
return isSpacePhone(length);
|
||||
} else if (contentType == TYPE_BANK_CARD) {
|
||||
return isSpaceCard(length);
|
||||
} else if (contentType == TYPE_ID_CARD) {
|
||||
return isSpaceIDCard(length);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private boolean isSpacePhone(int length) {
|
||||
return length >= 4 && (length == 4 || (length + 1) % 5 == 0);
|
||||
}
|
||||
|
||||
private boolean isSpaceCard(int length) {
|
||||
return length % 5 == 0;
|
||||
}
|
||||
|
||||
private boolean isSpaceIDCard(int length) {
|
||||
return length > 6 && (length == 7 || (length - 2) % 5 == 0);
|
||||
}
|
||||
|
||||
}
|
||||
37
uikit/src/main/java/com/fengliyan/uikit/UiUtils.java
Normal file
37
uikit/src/main/java/com/fengliyan/uikit/UiUtils.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.fengliyan.uikit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
/**
|
||||
* Created by chenqihong on 2017/1/17.
|
||||
*/
|
||||
|
||||
public class UiUtils {
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
|
||||
*/
|
||||
public static int dip2px(Context context, float dpValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
|
||||
*/
|
||||
public static int px2dip(Context context, float pxValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (pxValue / scale + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查权限是否同意
|
||||
* @param permission 权限名称
|
||||
* @return true 具有该权限
|
||||
*/
|
||||
public static boolean checkPermission(Context context, String permission) {
|
||||
return context.getPackageManager().checkPermission(permission, context.getPackageName()) == PackageManager
|
||||
.PERMISSION_GRANTED;
|
||||
}
|
||||
}
|
||||
36
uikit/src/main/java/com/fengliyan/uikit/adboard/AdPages.java
Normal file
36
uikit/src/main/java/com/fengliyan/uikit/adboard/AdPages.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.fengliyan.uikit.adboard;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
/**
|
||||
* Created by abby on 2016/12/12.
|
||||
*/
|
||||
|
||||
public class AdPages {
|
||||
private ViewPager mAdvPager;
|
||||
private ViewGroup mGroup;
|
||||
private View mConvertView;
|
||||
private AdPagesManager mManager;
|
||||
public AdPages(Context context){
|
||||
mConvertView = View.inflate(context, R.layout.ad_pager_board, null);
|
||||
mAdvPager = (ViewPager)mConvertView.findViewById(R.id.ad_pager);
|
||||
mGroup = (ViewGroup)mConvertView.findViewById(R.id.view_group);
|
||||
mManager = new AdPagesManager(mAdvPager, mGroup, context);
|
||||
}
|
||||
|
||||
public AdPages addImageById(int id){
|
||||
mManager.addImageViewById(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public View convertView(){
|
||||
mManager.manage();
|
||||
return mConvertView;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
package com.fengliyan.uikit.adboard;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Parcelable;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.facebook.drawee.drawable.ScalingUtils;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Created by ChenQihong on 2016/11/9.
|
||||
*/
|
||||
|
||||
public class AdPagesManager {
|
||||
private View[] mViews = null;
|
||||
private ViewPager mAdvPager = null;
|
||||
private ViewGroup mGroup = null;
|
||||
private List<View> mAdvPics = new ArrayList<View>();
|
||||
private AtomicInteger mWhat = new AtomicInteger(0);
|
||||
private boolean isContinue = true;
|
||||
private Context mContext;
|
||||
private ImageClickListener mListener;
|
||||
public AdPagesManager(ViewPager advPager, ViewGroup group, Context context){
|
||||
this.mAdvPager = advPager;
|
||||
this.mContext = context;
|
||||
this.mGroup = group;
|
||||
}
|
||||
public void addImageViewById(int id){
|
||||
ImageView img = new ImageView(mContext);
|
||||
img.setBackgroundResource(id);
|
||||
mAdvPics.add(img);
|
||||
}
|
||||
|
||||
public void addImageViewByDrawable(Drawable d){
|
||||
ImageView img = new ImageView(mContext);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
img.setBackground(d);
|
||||
}else{
|
||||
img.setBackgroundDrawable(d);
|
||||
}
|
||||
|
||||
mAdvPics.add(img);
|
||||
}
|
||||
public void addVideoView(View view){
|
||||
mAdvPics.add(view);
|
||||
view.setClickable(true);
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mListener.onClick(v, mAdvPics.indexOf(v));
|
||||
}
|
||||
});
|
||||
}
|
||||
public void addImageViewByUrl(String url){
|
||||
SimpleDraweeView img = new SimpleDraweeView(mContext);
|
||||
Uri uri = Uri.parse(url);
|
||||
img.setImageURI(uri);
|
||||
img.getHierarchy().setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP);
|
||||
img.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mListener.onClick(view, mAdvPics.indexOf(view));
|
||||
}
|
||||
});
|
||||
|
||||
mAdvPics.add(img);
|
||||
}
|
||||
|
||||
public interface ImageClickListener{
|
||||
void onClick(View v, int position);
|
||||
}
|
||||
|
||||
public void setOnImageClickListener(ImageClickListener listener){
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public boolean hasImages(){
|
||||
return !mAdvPics.isEmpty();
|
||||
}
|
||||
|
||||
public void manage(){
|
||||
mViews = new ImageView[mAdvPics.size()];
|
||||
for (int i = 0; i < mAdvPics.size(); i++) {
|
||||
ImageView imageView = new ImageView(mContext);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(10, 10);
|
||||
params.setMargins(12, 0, 0, 0);
|
||||
imageView.setLayoutParams(params);
|
||||
imageView.setPadding(12, 12, 12, 12);
|
||||
mViews[i] = imageView;
|
||||
if (i == 0) {
|
||||
mViews[i]
|
||||
.setBackgroundResource(R.drawable.shape_banner_index_focus);
|
||||
} else {
|
||||
mViews[i]
|
||||
.setBackgroundResource(R.drawable.shape_banner_index_inactived);
|
||||
}
|
||||
|
||||
mGroup.addView(mViews[i]);
|
||||
}
|
||||
|
||||
mAdvPager.setAdapter(new AdvAdapter(mAdvPics));
|
||||
mAdvPager.setOnPageChangeListener(new GuidePageChangeListener());
|
||||
mAdvPager.setOnTouchListener(new View.OnTouchListener() {
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
isContinue = false;
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
isContinue = true;
|
||||
|
||||
break;
|
||||
default:
|
||||
isContinue = true;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// new Thread(new Runnable() {
|
||||
//
|
||||
// @Override
|
||||
// public void run() {
|
||||
// while (true) {
|
||||
// if (isContinue) {
|
||||
// viewHandler.sendEmptyMessage(mWhat.get());
|
||||
// whatOption();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }).start();
|
||||
}
|
||||
|
||||
private void whatOption() {
|
||||
mWhat.incrementAndGet();
|
||||
if (mWhat.get() > mViews.length - 1) {
|
||||
mWhat.getAndAdd(-4);
|
||||
}
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private final Handler viewHandler = new Handler() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
mAdvPager.setCurrentItem(msg.what);
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private final class GuidePageChangeListener implements ViewPager.OnPageChangeListener {
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int arg0) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int arg0, float arg1, int arg2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int arg0) {
|
||||
mWhat.getAndSet(arg0);
|
||||
for (int i = 0; i < mViews.length; i++) {
|
||||
mViews[arg0]
|
||||
.setBackgroundResource(R.drawable.shape_banner_index_focus);
|
||||
if (arg0 != i) {
|
||||
mViews[i]
|
||||
.setBackgroundResource(R.drawable.shape_banner_index_inactived);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class AdvAdapter extends PagerAdapter {
|
||||
private List<View> views = null;
|
||||
|
||||
public AdvAdapter(List<View> views) {
|
||||
this.views = views;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(View arg0, int arg1, Object arg2) {
|
||||
((ViewPager) arg0).removeView(views.get(arg1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpdate(View arg0) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return views.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(View arg0, int arg1) {
|
||||
((ViewPager) arg0).addView(views.get(arg1), 0);
|
||||
return views.get(arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(View arg0, Object arg1) {
|
||||
return arg0 == arg1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreState(Parcelable arg0, ClassLoader arg1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable saveState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startUpdate(View arg0) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void clear(){
|
||||
if(null != mAdvPics){
|
||||
mAdvPics.clear();
|
||||
mGroup.removeAllViews();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
package com.fengliyan.uikit.bottomedit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by abby on 2017/1/12.
|
||||
*/
|
||||
|
||||
public class BottomEdit extends LinearLayout implements View.OnClickListener{
|
||||
private Context mContext;
|
||||
private EditText mEditView;
|
||||
private View mEditLayout;
|
||||
private Button mSubmitButton;
|
||||
private List<Fragment> mFragmentList = new ArrayList<>();
|
||||
private View mBottomFragment;
|
||||
private View mEmojiView;
|
||||
private View mPhotoView;
|
||||
private View mGiftView;
|
||||
private View mVideoView;
|
||||
private View mBottomView;
|
||||
private View mFunctionalBottom;
|
||||
private Fragment mAddFragment;
|
||||
private InputMethodManager mInputMethodManager;
|
||||
private SubmitButtonClickListener mSubmitButtonClickedListener;
|
||||
private FunctionButtonClickListener mFunctionButtonClickedListener;
|
||||
|
||||
public interface SubmitButtonClickListener{
|
||||
void onSubmitButtonClicked(String message);
|
||||
}
|
||||
|
||||
public interface FunctionButtonClickListener{
|
||||
void onClicked(int index);
|
||||
}
|
||||
|
||||
public void setSubmitButtonClickedListener(SubmitButtonClickListener listener){
|
||||
mSubmitButtonClickedListener = listener;
|
||||
}
|
||||
|
||||
public void setFunctionButtonClickListener(FunctionButtonClickListener listener){
|
||||
mFunctionButtonClickedListener = listener;
|
||||
}
|
||||
|
||||
public BottomEdit(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View rootView = inflater.inflate(R.layout.bottom_edit_view, this, true);
|
||||
mEditView = (EditText) rootView.findViewById(R.id.edit_edit);
|
||||
mEditLayout = rootView.findViewById(R.id.edit_edit_layout);
|
||||
mBottomView = rootView.findViewById(R.id.edit_layout);
|
||||
mBottomFragment = rootView.findViewById(R.id.bottom_fragment_layout);
|
||||
mEmojiView = rootView.findViewById(R.id.edit_emoji);
|
||||
mPhotoView = rootView.findViewById(R.id.edit_image);
|
||||
mGiftView = rootView.findViewById(R.id.edit_gift);
|
||||
mVideoView = rootView.findViewById(R.id.edit_video);
|
||||
mSubmitButton = rootView.findViewById(R.id.eidt_submit_button);
|
||||
mFunctionalBottom = rootView.findViewById(R.id.edit_functional_bottom);
|
||||
mEmojiView.setOnClickListener(this);
|
||||
mPhotoView.setOnClickListener(this);
|
||||
mGiftView.setOnClickListener(this);
|
||||
mVideoView.setOnClickListener(this);
|
||||
mSubmitButton.setOnClickListener(this);
|
||||
mEditView.setOnFocusChangeListener(new OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View view, boolean b) {
|
||||
if(b){
|
||||
dismissBottomFragment();
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
checkEditEmpty();
|
||||
|
||||
mEditView.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
checkEditEmpty();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
mInputMethodManager = (InputMethodManager)mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
//View editLayout = rootView.findViewById(R.id.edit_layout);
|
||||
//controlKeyboardLayout(rootView, editLayout);
|
||||
}
|
||||
|
||||
private void checkEditEmpty(){
|
||||
if(TextUtils.isEmpty(mEditView.getText().toString())){
|
||||
mSubmitButton.setEnabled(false);
|
||||
}else{
|
||||
mSubmitButton.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setFragment(Fragment fragment){
|
||||
FragmentActivity fragmentActivity = (FragmentActivity)mContext;
|
||||
try {
|
||||
|
||||
if (!fragment.isAdded()) {
|
||||
fragmentActivity.getSupportFragmentManager().
|
||||
beginTransaction().
|
||||
add(R.id.detail_fragment_layout, fragment).
|
||||
show(fragment).
|
||||
commitAllowingStateLoss();
|
||||
|
||||
} else {
|
||||
fragmentActivity.getSupportFragmentManager().
|
||||
beginTransaction().
|
||||
show(fragment).
|
||||
commitAllowingStateLoss();
|
||||
|
||||
}
|
||||
}catch (IllegalStateException e){
|
||||
fragmentActivity.getSupportFragmentManager().
|
||||
beginTransaction().
|
||||
show(fragment).
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
public void setBottomFragmentHeight(int height){
|
||||
LinearLayout.LayoutParams params = (LayoutParams) mBottomFragment.getLayoutParams();
|
||||
params.height = height;
|
||||
mBottomFragment.setLayoutParams(params);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public BottomEdit addBottomFragment(Fragment fragment){
|
||||
mFragmentList.add(fragment);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setBottomFragment(Fragment fragment){
|
||||
FragmentActivity fragmentActivity = (FragmentActivity)mContext;
|
||||
try {
|
||||
if(null != mAddFragment){
|
||||
fragmentActivity.getSupportFragmentManager().
|
||||
beginTransaction().remove(mAddFragment).commit();
|
||||
}
|
||||
|
||||
if (!fragment.isAdded()) {
|
||||
fragmentActivity.getSupportFragmentManager().
|
||||
beginTransaction().
|
||||
add(R.id.bottom_fragment_layout, fragment).
|
||||
show(fragment).
|
||||
commitAllowingStateLoss();
|
||||
|
||||
} else {
|
||||
fragmentActivity.getSupportFragmentManager().
|
||||
beginTransaction().
|
||||
show(fragment).
|
||||
commitAllowingStateLoss();
|
||||
|
||||
}
|
||||
|
||||
mAddFragment = fragment;
|
||||
}catch (IllegalStateException e){
|
||||
fragmentActivity.getSupportFragmentManager().
|
||||
beginTransaction().
|
||||
show(fragment).
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
public EditText getEditText(){
|
||||
return mEditView;
|
||||
}
|
||||
|
||||
public void showBottomFragment(){
|
||||
mBottomFragment.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
public void dismissBottomFragment(){
|
||||
FragmentActivity fragmentActivity = (FragmentActivity)mContext;
|
||||
Iterator<Fragment> iterator = mFragmentList.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Fragment fragment = iterator.next();
|
||||
fragmentActivity.getSupportFragmentManager().
|
||||
beginTransaction().remove(fragment).commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
//LinearLayout.LayoutParams params = (LayoutParams) mBottomFragment.getLayoutParams();
|
||||
//params.height = 0;
|
||||
//mBottomFragment.setLayoutParams(params);
|
||||
}
|
||||
|
||||
public void dismissAllBottom(){
|
||||
|
||||
mEditView.clearFocus();
|
||||
dismissBottomFragment();
|
||||
mInputMethodManager.hideSoftInputFromWindow(mEditView.getWindowToken(),0);
|
||||
}
|
||||
|
||||
public void dimissBottomFunction(){
|
||||
dismissAllBottom();
|
||||
mFunctionalBottom.setVisibility(GONE);
|
||||
}
|
||||
|
||||
public void dismissInput(){
|
||||
mEditView.clearFocus();
|
||||
mBottomView.setVisibility(GONE);
|
||||
mInputMethodManager.hideSoftInputFromWindow(mEditView.getWindowToken(),0);
|
||||
}
|
||||
|
||||
public void showInput(){
|
||||
mBottomView.setVisibility(VISIBLE);
|
||||
mEditView.clearFocus();
|
||||
mInputMethodManager.showSoftInput(mEditView, 0);
|
||||
}
|
||||
|
||||
public void dimissVideo(){
|
||||
mVideoView.setVisibility(GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if(view == mEmojiView){
|
||||
showBottomFragment();
|
||||
Fragment fragment = mFragmentList.get(0);
|
||||
setBottomFragment(fragment);
|
||||
mEditView.clearFocus();
|
||||
mInputMethodManager.hideSoftInputFromWindow(mEditView.getWindowToken(),0);
|
||||
if(null != mFunctionButtonClickedListener){
|
||||
mFunctionButtonClickedListener.onClicked(2);
|
||||
}
|
||||
|
||||
}else if(view == mSubmitButton){
|
||||
if(null != mSubmitButtonClickedListener){
|
||||
mSubmitButtonClickedListener.onSubmitButtonClicked(mEditView.getText().toString());
|
||||
}
|
||||
}else if(view == mPhotoView){
|
||||
mEditView.clearFocus();
|
||||
if(null != mFunctionButtonClickedListener){
|
||||
mFunctionButtonClickedListener.onClicked(1);
|
||||
}
|
||||
}else if(view == mGiftView){
|
||||
showBottomFragment();
|
||||
Fragment fragment = mFragmentList.get(1);
|
||||
setBottomFragment(fragment);
|
||||
mEditView.clearFocus();
|
||||
mInputMethodManager.hideSoftInputFromWindow(mEditView.getWindowToken(),0);
|
||||
if(null != mFunctionButtonClickedListener){
|
||||
mFunctionButtonClickedListener.onClicked(3);
|
||||
}
|
||||
}else if(view == mVideoView){
|
||||
mEditView.clearFocus();
|
||||
if(null != mFunctionButtonClickedListener){
|
||||
mFunctionButtonClickedListener.onClicked(4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.fengliyan.uikit.bottompost;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
/**
|
||||
* Created by abby on 2017/6/25.
|
||||
*/
|
||||
|
||||
public class BottomPost extends LinearLayout {
|
||||
private Context mContext;
|
||||
private ImageView mAddPhoto;
|
||||
private Button mPostButton;
|
||||
|
||||
public BottomPost(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View rootView = inflater.inflate(R.layout.bottom_post_view, this, true);
|
||||
View postLayout = rootView.findViewById(R.id.post_layout);
|
||||
mPostButton = (Button) rootView.findViewById(R.id.bottom_post_button);
|
||||
mAddPhoto = (ImageView) rootView.findViewById(R.id.bottom_add_photos);
|
||||
|
||||
controlKeyboardLayout(postLayout, postLayout);
|
||||
}
|
||||
|
||||
public void setFragment(Fragment fragment) {
|
||||
FragmentActivity fragmentActivity = (FragmentActivity) mContext;
|
||||
try {
|
||||
if (!fragment.isAdded()) {
|
||||
fragmentActivity.getSupportFragmentManager().
|
||||
beginTransaction().
|
||||
add(R.id.post_fragment_layout, fragment).
|
||||
show(fragment).
|
||||
commit();
|
||||
|
||||
} else {
|
||||
fragmentActivity.getSupportFragmentManager().
|
||||
beginTransaction().
|
||||
show(fragment).
|
||||
commit();
|
||||
|
||||
}
|
||||
}catch (IllegalStateException e){
|
||||
fragmentActivity.getSupportFragmentManager().
|
||||
beginTransaction().
|
||||
show(fragment).
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
public void controlKeyboardLayout(final View root,
|
||||
final View editLayout) {
|
||||
root.getViewTreeObserver().addOnGlobalLayoutListener(
|
||||
new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
Rect rect=new Rect();
|
||||
root.getWindowVisibleDisplayFrame(rect);
|
||||
int rootInVisibleHeigh=root.getRootView().getHeight()-rect.bottom;
|
||||
|
||||
if (rootInVisibleHeigh > 100) {
|
||||
int[] location = new int[2];
|
||||
editLayout.getLocationInWindow(location);
|
||||
int srollHeight = (location[1] + editLayout.getHeight()) - rect.bottom;
|
||||
if(srollHeight != 0) {
|
||||
//root.scrollTo(0, srollHeight);
|
||||
root.setPadding(0, 0, 0 , srollHeight);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
//root.scrollTo(0, 0);
|
||||
root.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
351
uikit/src/main/java/com/fengliyan/uikit/bottomtab/BottomTab.java
Normal file
351
uikit/src/main/java/com/fengliyan/uikit/bottomtab/BottomTab.java
Normal file
@@ -0,0 +1,351 @@
|
||||
package com.fengliyan.uikit.bottomtab;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.fengliyan.base.base.utils.NotificationBadgesUtils;
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.UiUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by ChenQihong on 2016/12/13.
|
||||
*/
|
||||
|
||||
public class BottomTab extends LinearLayout implements View.OnClickListener {
|
||||
private Context mContext;
|
||||
private FrameLayout mFragmentLayout;
|
||||
public List<Fragment> mFragmentList = new ArrayList<>();
|
||||
private int mCurrentPosition;
|
||||
private TabClickListener mListener;
|
||||
private VideoClickListener mVideoClickListener;
|
||||
private View mMainLayout;
|
||||
private View mDynamicLayout;
|
||||
private View mSettingsLayout;
|
||||
private View mNewsLayout;
|
||||
private View mVideoLogo;
|
||||
private View mVideoText;
|
||||
private View mVideoLayout;
|
||||
private View mVideoCancel;
|
||||
// private View mVideoPoint;
|
||||
// private View mVideoFrame;
|
||||
private TextView mNewsCountView;
|
||||
private TextView mSettingCountView;
|
||||
private int mTabIndex;
|
||||
private boolean isMale;
|
||||
private SimpleDraweeView bottom_tab_news_image;
|
||||
private String avatar;
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
||||
if (view == mMainLayout) { //首页
|
||||
change(0);
|
||||
mTabIndex = 0;
|
||||
} else if (view == mDynamicLayout) { //动态
|
||||
change(1);
|
||||
mTabIndex = 1;
|
||||
} else if (view == mSettingsLayout) { //我的
|
||||
change(4);
|
||||
mTabIndex = 4;
|
||||
} else if (view == mNewsLayout) { //消息
|
||||
change(3);
|
||||
mTabIndex = 3;
|
||||
} else if (view == mVideoLayout) { //榜单
|
||||
// if(null != mVideoClickListener){
|
||||
// mVideoClickListener.onVideoClick();
|
||||
// }
|
||||
change(2);
|
||||
mTabIndex = 2;
|
||||
}
|
||||
}
|
||||
|
||||
public interface TabClickListener {
|
||||
void onClick(int position, int currentPosition);
|
||||
}
|
||||
|
||||
public interface VideoClickListener {
|
||||
void onVideoClick();
|
||||
}
|
||||
|
||||
public void setVideoClickListener(VideoClickListener listener) {
|
||||
mVideoClickListener = listener;
|
||||
}
|
||||
|
||||
// public void startFlick(){
|
||||
// View view = mVideoPoint;
|
||||
// Animation alphaAnimation = new AlphaAnimation( 1, 0.4f );
|
||||
// alphaAnimation.setDuration( 300 );
|
||||
// alphaAnimation.setInterpolator( new LinearInterpolator( ) );
|
||||
// alphaAnimation.setRepeatCount( Animation.INFINITE );
|
||||
// alphaAnimation.setRepeatMode( Animation.REVERSE );
|
||||
// view.startAnimation( alphaAnimation );
|
||||
// }
|
||||
|
||||
// public void stopFlick(){
|
||||
// mVideoPoint.clearAnimation();
|
||||
// }
|
||||
|
||||
public Fragment getFragment(int index) {
|
||||
return mFragmentList.get(index);
|
||||
}
|
||||
|
||||
public void setOnTabClickListener(TabClickListener listener) {
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
public BottomTab(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.bottom_tab_view1, this, true);
|
||||
mMainLayout = findViewById(R.id.bottom_tab_main_layout);
|
||||
mMainLayout.setOnClickListener(this);
|
||||
mDynamicLayout = findViewById(R.id.bottom_tab_dynamic_layout);
|
||||
mDynamicLayout.setOnClickListener(this);
|
||||
mNewsLayout = findViewById(R.id.bottom_tab_news_layout);
|
||||
mNewsLayout.setOnClickListener(this);
|
||||
mSettingsLayout = findViewById(R.id.bottom_tab_settings_layout);
|
||||
mSettingsLayout.setOnClickListener(this);
|
||||
mNewsCountView = findViewById(R.id.bottom_tab_news_count);
|
||||
mSettingCountView = findViewById(R.id.bottom_tab_settings_count);
|
||||
mVideoLayout = findViewById(R.id.bottom_tab_live_layout);
|
||||
// mVideoLogo = findViewById(R.id.bottom_tab_live_image);
|
||||
bottom_tab_news_image = findViewById(R.id.bottom_tab_news_image);
|
||||
bottom_tab_news_image.setImageResource(R.drawable.ic_tab_news1);
|
||||
// mVideoPoint = findViewById(R.id.main_fragment_point);
|
||||
// mVideoFrame = findViewById(R.id.main_fragment_video);
|
||||
mVideoLayout.setOnClickListener(this);
|
||||
change(0);
|
||||
}
|
||||
|
||||
public void enableVideo() {
|
||||
if (isMale) {
|
||||
return;
|
||||
}
|
||||
|
||||
mVideoLayout.setVisibility(VISIBLE);
|
||||
mVideoLogo.setVisibility(VISIBLE);
|
||||
// mVideoPoint.setVisibility(VISIBLE);
|
||||
// startFlick();
|
||||
}
|
||||
|
||||
public void disableVideo() {
|
||||
if (isMale) {
|
||||
return;
|
||||
}
|
||||
|
||||
mVideoLayout.setVisibility(VISIBLE);
|
||||
mVideoLogo.setVisibility(VISIBLE);
|
||||
// stopFlick();
|
||||
// mVideoPoint.setVisibility(GONE);
|
||||
}
|
||||
|
||||
public void dismissVideoButton() {
|
||||
mVideoLayout.setVisibility(GONE);
|
||||
mVideoLogo.setVisibility(GONE);
|
||||
// mVideoPoint.setVisibility(GONE);
|
||||
}
|
||||
|
||||
|
||||
public void addTab(Fragment fragment) {
|
||||
mFragmentList.add(fragment);
|
||||
if (fragment == null || fragment.isAdded()) {
|
||||
return;
|
||||
}
|
||||
FragmentActivity activity = (FragmentActivity) mContext;
|
||||
activity.getSupportFragmentManager().beginTransaction()
|
||||
.add(R.id.main_fragment_layout, fragment, fragment.getClass().getName()).hide(fragment).commit();
|
||||
|
||||
}
|
||||
|
||||
public void change(int position) {
|
||||
if (!(mContext instanceof FragmentActivity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (position < mFragmentList.size()) {
|
||||
Fragment targetFragment = mFragmentList.get(position);
|
||||
Fragment currentFragment = mFragmentList.get(mCurrentPosition);
|
||||
FragmentActivity activity = (FragmentActivity) mContext;
|
||||
if (activity.getSupportFragmentManager() != null) {
|
||||
try {
|
||||
activity.getSupportFragmentManager().beginTransaction().hide(currentFragment).show(targetFragment).commitAllowingStateLoss();
|
||||
} catch (IllegalStateException e) {
|
||||
activity.getSupportFragmentManager().beginTransaction().hide(currentFragment).show(targetFragment).commitAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null != mListener) {
|
||||
mListener.onClick(position, mCurrentPosition);
|
||||
}
|
||||
|
||||
tabStyleChange(position, mCurrentPosition);
|
||||
mCurrentPosition = position;
|
||||
}
|
||||
|
||||
private void tabStyleChange(int targetPosition, int currentPosition) {
|
||||
if (0 == targetPosition) {
|
||||
((TextView) findViewById(R.id.bottom_tab_main_text)).setTextColor(ContextCompat.getColor(mContext, R.color.zhimi_bottom_text_selected));
|
||||
((ImageView) findViewById(R.id.bottom_tab_main_image)).setImageResource(R.drawable.ic_tab_home2);
|
||||
setImageLayoutParams(findViewById(R.id.bottom_tab_main_image), 28);
|
||||
}
|
||||
if (1 == targetPosition) {
|
||||
((TextView) findViewById(R.id.bottom_tab_dynamic_text)).setTextColor(ContextCompat.getColor(mContext, R.color.zhimi_bottom_text_selected));
|
||||
((ImageView) findViewById(R.id.bottom_tab_dynamic_image)).setImageResource(R.drawable.ic_tab_dynamic2);
|
||||
setImageLayoutParams(findViewById(R.id.bottom_tab_dynamic_image), 28);
|
||||
} else if (3 == targetPosition) {
|
||||
((TextView) findViewById(R.id.bottom_tab_news_text)).setTextColor(ContextCompat.getColor(mContext, R.color.zhimi_bottom_text_selected));
|
||||
if (TextUtils.isEmpty(avatar)) {
|
||||
bottom_tab_news_image.setImageResource(R.drawable.ic_tab_news2);
|
||||
setImageLayoutParams(bottom_tab_news_image, 28);
|
||||
} else {
|
||||
setImageLayoutParams(bottom_tab_news_image, 28);
|
||||
}
|
||||
} else if (4 == targetPosition) {
|
||||
((TextView) findViewById(R.id.bottom_tab_settings_text)).setTextColor(ContextCompat.getColor(mContext, R.color.zhimi_bottom_text_selected));
|
||||
((ImageView) findViewById(R.id.bottom_tab_settings_image)).setImageResource(R.drawable.ic_tab_my2);
|
||||
setImageLayoutParams(findViewById(R.id.bottom_tab_settings_image), 28);
|
||||
} else if (2 == targetPosition) {
|
||||
((TextView) findViewById(R.id.bottom_tab_ranking_text)).setTextColor(ContextCompat.getColor(mContext, R.color.zhimi_bottom_text_selected));
|
||||
((ImageView) findViewById(R.id.main_fragment_video)).setImageResource(R.drawable.ic_tab_ranking2);
|
||||
setImageLayoutParams(findViewById(R.id.main_fragment_video), 28);
|
||||
}
|
||||
|
||||
if (targetPosition == currentPosition) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == currentPosition) {
|
||||
((TextView) findViewById(R.id.bottom_tab_main_text)).setTextColor(ContextCompat.getColor(mContext, R.color.zhimi_bottom_text));
|
||||
((ImageView) findViewById(R.id.bottom_tab_main_image)).setImageResource(R.drawable.ic_tab_home1);
|
||||
setImageLayoutParams(findViewById(R.id.bottom_tab_main_image), 24);
|
||||
} else if (1 == currentPosition) {
|
||||
((TextView) findViewById(R.id.bottom_tab_dynamic_text)).setTextColor(ContextCompat.getColor(mContext, R.color.zhimi_bottom_text));
|
||||
((ImageView) findViewById(R.id.bottom_tab_dynamic_image)).setImageResource(R.drawable.ic_tab_dynamic1);
|
||||
setImageLayoutParams(findViewById(R.id.bottom_tab_dynamic_image), 24);
|
||||
} else if (3 == currentPosition) {
|
||||
((TextView) findViewById(R.id.bottom_tab_news_text)).setTextColor(ContextCompat.getColor(mContext, R.color.zhimi_bottom_text));
|
||||
if (TextUtils.isEmpty(avatar)) {
|
||||
bottom_tab_news_image.setImageResource(R.drawable.ic_tab_news1);
|
||||
setImageLayoutParams(bottom_tab_news_image, 24);
|
||||
} else {
|
||||
setImageLayoutParams(bottom_tab_news_image, 28);
|
||||
}
|
||||
} else if (4 == currentPosition) {
|
||||
((TextView) findViewById(R.id.bottom_tab_settings_text)).setTextColor(ContextCompat.getColor(mContext, R.color.zhimi_bottom_text));
|
||||
((ImageView) findViewById(R.id.bottom_tab_settings_image)).setImageResource(R.drawable.ic_tab_my1);
|
||||
setImageLayoutParams(findViewById(R.id.bottom_tab_settings_image), 24);
|
||||
} else if (2 == currentPosition) {
|
||||
((TextView) findViewById(R.id.bottom_tab_ranking_text)).setTextColor(ContextCompat.getColor(mContext, R.color.zhimi_bottom_text));
|
||||
((ImageView) findViewById(R.id.main_fragment_video)).setImageResource(R.drawable.ic_tab_ranking1);
|
||||
setImageLayoutParams(findViewById(R.id.main_fragment_video), 24);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置图标大小
|
||||
*
|
||||
* @param view tab图标
|
||||
* @param dp 大小
|
||||
*/
|
||||
private void setImageLayoutParams(View view, int dp) {
|
||||
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
|
||||
layoutParams.width = UiUtils.dip2px(mContext, dp);
|
||||
layoutParams.height = UiUtils.dip2px(mContext, dp);
|
||||
view.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
public int getTabIndex() {
|
||||
return mCurrentPosition;
|
||||
}
|
||||
|
||||
public void setNewCount(int num, String avatar) {
|
||||
this.avatar = avatar;
|
||||
if (0 == num) {
|
||||
mNewsCountView.setVisibility(GONE);
|
||||
} else {
|
||||
if (num > 99) {
|
||||
mNewsCountView.setText("99");
|
||||
} else {
|
||||
mNewsCountView.setText(num + "");
|
||||
}
|
||||
mNewsCountView.setVisibility(VISIBLE);
|
||||
}
|
||||
Log.i("TAG", "setNewCount: ----------->" + avatar);
|
||||
if (!TextUtils.isEmpty(avatar)) {
|
||||
bottom_tab_news_image.setImageURI(avatar);
|
||||
setImageLayoutParams(bottom_tab_news_image, 28);
|
||||
} else {
|
||||
if (3 == mCurrentPosition) {
|
||||
bottom_tab_news_image.setImageResource(R.drawable.ic_tab_news2);
|
||||
setImageLayoutParams(bottom_tab_news_image, 28);
|
||||
} else {
|
||||
bottom_tab_news_image.setImageResource(R.drawable.ic_tab_news1);
|
||||
setImageLayoutParams(bottom_tab_news_image, 24);
|
||||
}
|
||||
}
|
||||
|
||||
// String manufacturer = Build.MANUFACTURER;
|
||||
// if (manufacturer != null && manufacturer.length() > 0) {
|
||||
// String phone_type = manufacturer.toLowerCase();
|
||||
// if ("huawei".equals(phone_type)) {
|
||||
// showHuaWeiDeskMark(num);
|
||||
// }
|
||||
// }
|
||||
NotificationBadgesUtils.setCount(num, mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param number 显示桌面角标数量 0 则隐藏
|
||||
* 显示华为角标
|
||||
*/
|
||||
private void showHuaWeiDeskMark(int number) {
|
||||
try {
|
||||
Bundle extra = new Bundle();
|
||||
extra.putString("package", getContext().getPackageName());
|
||||
extra.putString("class", "com.fengliyan.tianlesue.view.main.CoverActivity");
|
||||
extra.putInt("badgenumber", number);
|
||||
getContext().getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, extra);
|
||||
} catch (Exception ignored) {
|
||||
ignored.printStackTrace();
|
||||
Log.e("err", "showHuaWeiDeskMark is Error");
|
||||
}
|
||||
}
|
||||
|
||||
public void setSettingsCount(int num) {
|
||||
if (0 == num) {
|
||||
mSettingCountView.setVisibility(GONE);
|
||||
} else {
|
||||
mSettingCountView.setText(num + "");
|
||||
mSettingCountView.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void maleStyle() {
|
||||
mVideoLayout.setVisibility(GONE);
|
||||
mVideoLogo.setVisibility(GONE);
|
||||
// mVideoPoint.setVisibility(GONE);
|
||||
// mVideoFrame.setVisibility(GONE);
|
||||
isMale = true;
|
||||
}
|
||||
}
|
||||
27
uikit/src/main/java/com/fengliyan/uikit/crop/Crop.java
Normal file
27
uikit/src/main/java/com/fengliyan/uikit/crop/Crop.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.fengliyan.uikit.crop;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
/**
|
||||
* Created by neuyuandaima on 2016/1/13.
|
||||
*
|
||||
* @deprecated Use {@link CropHelper} instead of it.
|
||||
*/
|
||||
public interface Crop {
|
||||
/**
|
||||
* 打开图库,由用户选择照片后裁剪
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* 裁剪指定的资源
|
||||
*
|
||||
* @param uri 图片uri
|
||||
*/
|
||||
void start(Uri uri);
|
||||
|
||||
//展示结果
|
||||
void onActivityResult(int requestCode, int resultCode, Intent data, CropCallback callback);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.fengliyan.uikit.crop;
|
||||
|
||||
/**
|
||||
* 图片裁剪回调
|
||||
* Created by neuyuandaima on 2016/1/13.
|
||||
*/
|
||||
public interface CropCallback {
|
||||
|
||||
/**
|
||||
* 裁剪后的回调
|
||||
* @param path 当成功保存时,返回图片路径,否则为null
|
||||
*/
|
||||
void OnReceiveBitmap(String path);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.fengliyan.uikit.crop;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
/**
|
||||
* Created by neuyuandaima on 2016/1/13.
|
||||
* 剪切工厂类
|
||||
*
|
||||
* @deprecated Use {@link CropHelper} instead of it.
|
||||
*/
|
||||
public class CropFactory {
|
||||
/**
|
||||
* 传入activity的Contact
|
||||
*
|
||||
* @param activity 传入参数
|
||||
* @return 返回Contact接口对象
|
||||
*/
|
||||
public static Crop newCrop(Activity activity) {
|
||||
return new CropImpl(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 传入fragment的Contact
|
||||
*
|
||||
* @param fragment 传入参数
|
||||
* @return 返回Contact接口对象
|
||||
*/
|
||||
public static Crop newCrop(Fragment fragment) {
|
||||
return new CropImpl(fragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* 传入fragment的Contact
|
||||
*
|
||||
* @param fragment 传入参数
|
||||
* @return 返回Contact接口对象
|
||||
*/
|
||||
public static Crop newCrop(android.app.Fragment fragment) {
|
||||
return new CropImpl(fragment);
|
||||
}
|
||||
}
|
||||
118
uikit/src/main/java/com/fengliyan/uikit/crop/CropHelper.java
Normal file
118
uikit/src/main/java/com/fengliyan/uikit/crop/CropHelper.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package com.fengliyan.uikit.crop;
|
||||
|
||||
import android.Manifest.permission;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.fengliyan.uikit.UiUtils;
|
||||
import com.fengliyan.uikit.crop.app.CropActivity;
|
||||
import com.fengliyan.uikit.crop.app.CropExtras;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 使用裁剪功能的帮助类,在CropHelper的构造中需传入裁剪结果的回调,当用户取消等,返回null;裁剪保存后,返回图片的路径。
|
||||
* Created by yangjinbo on 2016/2/26.
|
||||
*/
|
||||
public class CropHelper {
|
||||
|
||||
private static final int REQ_CROP = 231;
|
||||
|
||||
private Activity mActivity;
|
||||
|
||||
|
||||
public CropHelper(Activity activity) {
|
||||
if (activity == null) {
|
||||
throw new IllegalArgumentException("activity cannot be null!");
|
||||
}
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 开始裁剪。将在CropHelper构造方法的CropCallback回调方法中返回图片路径。
|
||||
* 需要在调用的activity的{@link Activity#onActivityResult(int, int, Intent)}
|
||||
* 或者fragment中的{@link Fragment#onActivityResult(int, int, Intent)}中调用
|
||||
* {@link #handleResult(int, int, Intent, CropCallback)}处理返回的结果。
|
||||
*
|
||||
* @param uri 裁剪源图片,如果为null,会打开相册由用户自己选择。
|
||||
* @see #start(Object, Uri)
|
||||
*/
|
||||
public void start(Uri uri) {
|
||||
start(null, uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始裁剪图片
|
||||
*
|
||||
* @param fragment onActivityResult(int, int, Intent)回调所在的fragment
|
||||
* @param uri 图片源文件
|
||||
* @see #start(Uri)
|
||||
*/
|
||||
public void start(final Object fragment, final Uri uri) {
|
||||
if ((fragment != null) && !(fragment instanceof Fragment) && !(fragment instanceof android.app.Fragment)) {
|
||||
throw new IllegalArgumentException("if fragment isn't null," +
|
||||
" fragment must be android.support.v4.app.Fragment or android.app.Fragment! ");
|
||||
}
|
||||
|
||||
if (!UiUtils.checkPermission(mActivity, permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
return;
|
||||
}
|
||||
Intent intent = new Intent(mActivity, CropActivity.class);
|
||||
intent.setData(uri);
|
||||
setIntentExtra(intent);
|
||||
if (fragment != null) {
|
||||
if (fragment instanceof Fragment) {
|
||||
((Fragment) fragment).startActivityForResult(intent, REQ_CROP);
|
||||
} else {
|
||||
((android.app.Fragment) fragment).startActivityForResult(intent, REQ_CROP);
|
||||
}
|
||||
} else {
|
||||
mActivity.startActivityForResult(intent, REQ_CROP);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void setIntentExtra(Intent intent) {
|
||||
intent.putExtra(CropExtras.KEY_ASPECT_X, 1);
|
||||
intent.putExtra(CropExtras.KEY_ASPECT_Y, 1);
|
||||
intent.putExtra(CropExtras.KEY_OUTPUT_FORMAT, "jpg");
|
||||
intent.putExtra(CropExtras.KEY_RETURN_DATA, false);
|
||||
File file = createImageFile();
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
|
||||
}
|
||||
|
||||
|
||||
private File createImageFile() {
|
||||
try {
|
||||
return File.createTempFile("temp", ".jpg", mActivity.getExternalCacheDir());
|
||||
} catch (IOException e) {
|
||||
return new File(mActivity.getCacheDir().getAbsolutePath()
|
||||
+ "/" + System.currentTimeMillis() + ".jpg");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理裁剪后的结果,在Activity或者Fragment中的onActivityResult(int, int, Intent)调用
|
||||
*
|
||||
* @return true 已处理,false 未处理
|
||||
*/
|
||||
public boolean handleResult(int requestCode, int resultCode, Intent data, CropCallback cropCallback) {
|
||||
|
||||
if (requestCode != REQ_CROP) {
|
||||
return false;
|
||||
}
|
||||
if (resultCode != Activity.RESULT_OK || data == null) {
|
||||
cropCallback.OnReceiveBitmap(null);
|
||||
} else {
|
||||
cropCallback.OnReceiveBitmap(data.getData().getPath());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
116
uikit/src/main/java/com/fengliyan/uikit/crop/CropImpl.java
Normal file
116
uikit/src/main/java/com/fengliyan/uikit/crop/CropImpl.java
Normal file
@@ -0,0 +1,116 @@
|
||||
package com.fengliyan.uikit.crop;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.fengliyan.uikit.crop.app.CropActivity;
|
||||
import com.fengliyan.uikit.crop.app.CropExtras;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by neuyuandaima on 2016/1/13.\
|
||||
* crop实现类
|
||||
*
|
||||
* @deprecated Use {@link CropHelper} instead of it.
|
||||
*/
|
||||
public class CropImpl implements Crop {
|
||||
|
||||
private static final int SELECT_PICTURE = 6019; // request code for picker 和用户的特征码区分开
|
||||
|
||||
private static final int CROP_PICTURE = 6020; // request code for crop 和用户的特征码区分开
|
||||
|
||||
|
||||
//持有activity
|
||||
private Activity mActivity;
|
||||
//持有fragment
|
||||
private Fragment mSupportFragment;
|
||||
//持有fragment
|
||||
private android.app.Fragment mFragment;
|
||||
|
||||
//构造器
|
||||
public CropImpl(Activity activity) {
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
public CropImpl(Fragment fragment) {
|
||||
mSupportFragment = fragment;
|
||||
mActivity = mSupportFragment.getActivity();
|
||||
}
|
||||
|
||||
public CropImpl(android.app.Fragment fragment) {
|
||||
mFragment = fragment;
|
||||
mActivity = mFragment.getActivity();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
Intent intent = new Intent(mActivity, CropActivity.class);
|
||||
setIntentExtra(intent);
|
||||
if (mFragment != null) {
|
||||
mFragment.startActivityForResult(intent, SELECT_PICTURE);
|
||||
} else if (mSupportFragment != null) {
|
||||
mSupportFragment.startActivityForResult(intent, SELECT_PICTURE);
|
||||
} else {
|
||||
mActivity.startActivityForResult(intent, SELECT_PICTURE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setIntentExtra(Intent intent) {
|
||||
intent.putExtra(CropExtras.KEY_ASPECT_X, 1);
|
||||
intent.putExtra(CropExtras.KEY_ASPECT_Y, 1);
|
||||
intent.putExtra(CropExtras.KEY_OUTPUT_FORMAT, "jpg");
|
||||
intent.putExtra(CropExtras.KEY_RETURN_DATA, false);
|
||||
File file = newImgFile();
|
||||
if (file == null) {
|
||||
file = new File(mActivity.getCacheDir().getAbsolutePath()
|
||||
+ "/" + System.currentTimeMillis() + ".jpg");
|
||||
}
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Uri uri) {
|
||||
Intent intent = new Intent(CropActivity.CROP_ACTION, uri, mActivity, CropActivity.class);
|
||||
setIntentExtra(intent);
|
||||
if (mFragment != null) {
|
||||
mFragment.startActivityForResult(intent, CROP_PICTURE);
|
||||
} else if (mSupportFragment != null) {
|
||||
mSupportFragment.startActivityForResult(intent, CROP_PICTURE);
|
||||
} else {
|
||||
mActivity.startActivityForResult(intent, CROP_PICTURE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data, CropCallback callback) {
|
||||
if (requestCode != CROP_PICTURE && requestCode != SELECT_PICTURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
callback.OnReceiveBitmap(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
callback.OnReceiveBitmap(null);
|
||||
} else {
|
||||
callback.OnReceiveBitmap(data.getData().getPath());
|
||||
}
|
||||
}
|
||||
|
||||
private File newImgFile() {
|
||||
try {
|
||||
return File.createTempFile("temp", ".jpg", mActivity.getExternalCacheDir());
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.fengliyan.uikit.crop.app;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Maintains invariant that inner rectangle is constrained to be within the
|
||||
* outer, rotated rectangle.
|
||||
*/
|
||||
public class BoundedRect {
|
||||
private float rot;
|
||||
private RectF outer;
|
||||
private RectF inner;
|
||||
private float[] innerRotated;
|
||||
|
||||
public BoundedRect(float rotation, Rect outerRect, Rect innerRect) {
|
||||
rot = rotation;
|
||||
outer = new RectF(outerRect);
|
||||
inner = new RectF(innerRect);
|
||||
innerRotated = CropMath.getCornersFromRect(inner);
|
||||
rotateInner();
|
||||
if (!isConstrained())
|
||||
reconstrain();
|
||||
}
|
||||
|
||||
public BoundedRect(float rotation, RectF outerRect, RectF innerRect) {
|
||||
rot = rotation;
|
||||
outer = new RectF(outerRect);
|
||||
inner = new RectF(innerRect);
|
||||
innerRotated = CropMath.getCornersFromRect(inner);
|
||||
rotateInner();
|
||||
if (!isConstrained())
|
||||
reconstrain();
|
||||
}
|
||||
|
||||
public void resetTo(float rotation, RectF outerRect, RectF innerRect) {
|
||||
rot = rotation;
|
||||
outer.set(outerRect);
|
||||
inner.set(innerRect);
|
||||
innerRotated = CropMath.getCornersFromRect(inner);
|
||||
rotateInner();
|
||||
if (!isConstrained())
|
||||
reconstrain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets inner, and re-constrains it to fit within the rotated bounding rect.
|
||||
*/
|
||||
public void setInner(RectF newInner) {
|
||||
if (inner.equals(newInner))
|
||||
return;
|
||||
inner = newInner;
|
||||
innerRotated = CropMath.getCornersFromRect(inner);
|
||||
rotateInner();
|
||||
if (!isConstrained())
|
||||
reconstrain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets rotation, and re-constrains inner to fit within the rotated bounding rect.
|
||||
*/
|
||||
public void setRotation(float rotation) {
|
||||
if (rotation == rot)
|
||||
return;
|
||||
rot = rotation;
|
||||
innerRotated = CropMath.getCornersFromRect(inner);
|
||||
rotateInner();
|
||||
if (!isConstrained())
|
||||
reconstrain();
|
||||
}
|
||||
|
||||
public void setToInner(RectF r) {
|
||||
r.set(inner);
|
||||
}
|
||||
|
||||
public void setToOuter(RectF r) {
|
||||
r.set(outer);
|
||||
}
|
||||
|
||||
public RectF getInner() {
|
||||
return new RectF(inner);
|
||||
}
|
||||
|
||||
public RectF getOuter() {
|
||||
return new RectF(outer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to move the inner rectangle by (dx, dy). If this would cause it to leave
|
||||
* the bounding rectangle, snaps the inner rectangle to the edge of the bounding
|
||||
* rectangle.
|
||||
*/
|
||||
public void moveInner(float dx, float dy) {
|
||||
Matrix m0 = getInverseRotMatrix();
|
||||
|
||||
RectF translatedInner = new RectF(inner);
|
||||
translatedInner.offset(dx, dy);
|
||||
|
||||
float[] translatedInnerCorners = CropMath.getCornersFromRect(translatedInner);
|
||||
float[] outerCorners = CropMath.getCornersFromRect(outer);
|
||||
|
||||
m0.mapPoints(translatedInnerCorners);
|
||||
float[] correction = {
|
||||
0, 0
|
||||
};
|
||||
|
||||
// find correction vectors for corners that have moved out of bounds
|
||||
for (int i = 0; i < translatedInnerCorners.length; i += 2) {
|
||||
float correctedInnerX = translatedInnerCorners[i] + correction[0];
|
||||
float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
|
||||
if (!CropMath.inclusiveContains(outer, correctedInnerX, correctedInnerY)) {
|
||||
float[] badCorner = {
|
||||
correctedInnerX, correctedInnerY
|
||||
};
|
||||
float[] nearestSide = CropMath.closestSide(badCorner, outerCorners);
|
||||
float[] correctionVec =
|
||||
Utils.shortestVectorFromPointToLine(badCorner, nearestSide);
|
||||
correction[0] += correctionVec[0];
|
||||
correction[1] += correctionVec[1];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < translatedInnerCorners.length; i += 2) {
|
||||
float correctedInnerX = translatedInnerCorners[i] + correction[0];
|
||||
float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
|
||||
if (!CropMath.inclusiveContains(outer, correctedInnerX, correctedInnerY)) {
|
||||
float[] correctionVec = {
|
||||
correctedInnerX, correctedInnerY
|
||||
};
|
||||
CropMath.getEdgePoints(outer, correctionVec);
|
||||
correctionVec[0] -= correctedInnerX;
|
||||
correctionVec[1] -= correctedInnerY;
|
||||
correction[0] += correctionVec[0];
|
||||
correction[1] += correctionVec[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Set correction
|
||||
for (int i = 0; i < translatedInnerCorners.length; i += 2) {
|
||||
float correctedInnerX = translatedInnerCorners[i] + correction[0];
|
||||
float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
|
||||
// update translated corners with correction vectors
|
||||
translatedInnerCorners[i] = correctedInnerX;
|
||||
translatedInnerCorners[i + 1] = correctedInnerY;
|
||||
}
|
||||
|
||||
innerRotated = translatedInnerCorners;
|
||||
// reconstrain to update inner
|
||||
reconstrain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to resize the inner rectangle. If this would cause it to leave
|
||||
* the bounding rect, clips the inner rectangle to fit.
|
||||
*/
|
||||
public void resizeInner(RectF newInner) {
|
||||
Matrix m = getRotMatrix();
|
||||
Matrix m0 = getInverseRotMatrix();
|
||||
|
||||
float[] outerCorners = CropMath.getCornersFromRect(outer);
|
||||
m.mapPoints(outerCorners);
|
||||
float[] oldInnerCorners = CropMath.getCornersFromRect(inner);
|
||||
float[] newInnerCorners = CropMath.getCornersFromRect(newInner);
|
||||
RectF ret = new RectF(newInner);
|
||||
|
||||
for (int i = 0; i < newInnerCorners.length; i += 2) {
|
||||
float[] c = {
|
||||
newInnerCorners[i], newInnerCorners[i + 1]
|
||||
};
|
||||
float[] c0 = Arrays.copyOf(c, 2);
|
||||
m0.mapPoints(c0);
|
||||
if (!CropMath.inclusiveContains(outer, c0[0], c0[1])) {
|
||||
float[] outerSide = CropMath.closestSide(c, outerCorners);
|
||||
float[] pathOfCorner = {
|
||||
newInnerCorners[i], newInnerCorners[i + 1],
|
||||
oldInnerCorners[i], oldInnerCorners[i + 1]
|
||||
};
|
||||
float[] p = Utils.lineIntersect(pathOfCorner, outerSide);
|
||||
if (p == null) {
|
||||
// lines are parallel or not well defined, so don't resize
|
||||
p = new float[2];
|
||||
p[0] = oldInnerCorners[i];
|
||||
p[1] = oldInnerCorners[i + 1];
|
||||
}
|
||||
// relies on corners being in same order as method
|
||||
// getCornersFromRect
|
||||
switch (i) {
|
||||
case 0:
|
||||
case 1:
|
||||
ret.left = (p[0] > ret.left) ? p[0] : ret.left;
|
||||
ret.top = (p[1] > ret.top) ? p[1] : ret.top;
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
ret.right = (p[0] < ret.right) ? p[0] : ret.right;
|
||||
ret.top = (p[1] > ret.top) ? p[1] : ret.top;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
ret.right = (p[0] < ret.right) ? p[0] : ret.right;
|
||||
ret.bottom = (p[1] < ret.bottom) ? p[1] : ret.bottom;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
ret.left = (p[0] > ret.left) ? p[0] : ret.left;
|
||||
ret.bottom = (p[1] < ret.bottom) ? p[1] : ret.bottom;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
float[] retCorners = CropMath.getCornersFromRect(ret);
|
||||
m0.mapPoints(retCorners);
|
||||
innerRotated = retCorners;
|
||||
// reconstrain to update inner
|
||||
reconstrain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to resize the inner rectangle. If this would cause it to leave
|
||||
* the bounding rect, clips the inner rectangle to fit while maintaining
|
||||
* aspect ratio.
|
||||
*/
|
||||
public void fixedAspectResizeInner(RectF newInner) {
|
||||
Matrix m = getRotMatrix();
|
||||
Matrix m0 = getInverseRotMatrix();
|
||||
|
||||
float aspectW = inner.width();
|
||||
float aspectH = inner.height();
|
||||
float aspRatio = aspectW / aspectH;
|
||||
float[] corners = CropMath.getCornersFromRect(outer);
|
||||
|
||||
m.mapPoints(corners);
|
||||
float[] oldInnerCorners = CropMath.getCornersFromRect(inner);
|
||||
float[] newInnerCorners = CropMath.getCornersFromRect(newInner);
|
||||
|
||||
// find fixed corner
|
||||
int fixed = -1;
|
||||
if (inner.top == newInner.top) {
|
||||
if (inner.left == newInner.left)
|
||||
fixed = 0; // top left
|
||||
else if (inner.right == newInner.right)
|
||||
fixed = 2; // top right
|
||||
} else if (inner.bottom == newInner.bottom) {
|
||||
if (inner.right == newInner.right)
|
||||
fixed = 4; // bottom right
|
||||
else if (inner.left == newInner.left)
|
||||
fixed = 6; // bottom left
|
||||
}
|
||||
// no fixed corner, return without update
|
||||
if (fixed == -1)
|
||||
return;
|
||||
float widthSoFar = newInner.width();
|
||||
int moved = -1;
|
||||
for (int i = 0; i < newInnerCorners.length; i += 2) {
|
||||
float[] c = {
|
||||
newInnerCorners[i], newInnerCorners[i + 1]
|
||||
};
|
||||
float[] c0 = Arrays.copyOf(c, 2);
|
||||
m0.mapPoints(c0);
|
||||
if (!CropMath.inclusiveContains(outer, c0[0], c0[1])) {
|
||||
moved = i;
|
||||
if (moved == fixed)
|
||||
continue;
|
||||
float[] l2 = CropMath.closestSide(c, corners);
|
||||
float[] l1 = {
|
||||
newInnerCorners[i], newInnerCorners[i + 1],
|
||||
oldInnerCorners[i], oldInnerCorners[i + 1]
|
||||
};
|
||||
float[] p = Utils.lineIntersect(l1, l2);
|
||||
if (p == null) {
|
||||
// lines are parallel or not well defined, so set to old
|
||||
// corner
|
||||
p = new float[2];
|
||||
p[0] = oldInnerCorners[i];
|
||||
p[1] = oldInnerCorners[i + 1];
|
||||
}
|
||||
// relies on corners being in same order as method
|
||||
// getCornersFromRect
|
||||
float fixed_x = oldInnerCorners[fixed];
|
||||
float fixed_y = oldInnerCorners[fixed + 1];
|
||||
float newWidth = Math.abs(fixed_x - p[0]);
|
||||
float newHeight = Math.abs(fixed_y - p[1]);
|
||||
newWidth = Math.max(newWidth, aspRatio * newHeight);
|
||||
if (newWidth < widthSoFar)
|
||||
widthSoFar = newWidth;
|
||||
}
|
||||
}
|
||||
|
||||
float heightSoFar = widthSoFar / aspRatio;
|
||||
RectF ret = new RectF(inner);
|
||||
if (fixed == 0) {
|
||||
ret.right = ret.left + widthSoFar;
|
||||
ret.bottom = ret.top + heightSoFar;
|
||||
} else if (fixed == 2) {
|
||||
ret.left = ret.right - widthSoFar;
|
||||
ret.bottom = ret.top + heightSoFar;
|
||||
} else if (fixed == 4) {
|
||||
ret.left = ret.right - widthSoFar;
|
||||
ret.top = ret.bottom - heightSoFar;
|
||||
} else if (fixed == 6) {
|
||||
ret.right = ret.left + widthSoFar;
|
||||
ret.top = ret.bottom - heightSoFar;
|
||||
}
|
||||
float[] retCorners = CropMath.getCornersFromRect(ret);
|
||||
m0.mapPoints(retCorners);
|
||||
innerRotated = retCorners;
|
||||
// reconstrain to update inner
|
||||
reconstrain();
|
||||
}
|
||||
|
||||
// internal methods
|
||||
|
||||
private boolean isConstrained() {
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
if (!CropMath.inclusiveContains(outer, innerRotated[i], innerRotated[i + 1]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void reconstrain() {
|
||||
// innerRotated has been changed to have incorrect values
|
||||
CropMath.getEdgePoints(outer, innerRotated);
|
||||
Matrix m = getRotMatrix();
|
||||
float[] unrotated = Arrays.copyOf(innerRotated, 8);
|
||||
m.mapPoints(unrotated);
|
||||
inner = CropMath.trapToRect(unrotated);
|
||||
}
|
||||
|
||||
private void rotateInner() {
|
||||
Matrix m = getInverseRotMatrix();
|
||||
m.mapPoints(innerRotated);
|
||||
}
|
||||
|
||||
private Matrix getRotMatrix() {
|
||||
Matrix m = new Matrix();
|
||||
m.setRotate(rot, outer.centerX(), outer.centerY());
|
||||
return m;
|
||||
}
|
||||
|
||||
private Matrix getInverseRotMatrix() {
|
||||
Matrix m = new Matrix();
|
||||
m.setRotate(-rot, outer.centerX(), outer.centerY());
|
||||
return m;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,720 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.fengliyan.uikit.crop.app;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapRegionDecoder;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Activity for cropping an image.
|
||||
*/
|
||||
public class CropActivity extends Activity {
|
||||
private static final String TAG = "CropActivity";
|
||||
public static final String CROP_ACTION = "com.android.camera.action.CROP";
|
||||
private CropExtras mCropExtras = null;
|
||||
private LoadBitmapTask mLoadBitmapTask = null;
|
||||
|
||||
private int mOutputX = 0;
|
||||
private int mOutputY = 0;
|
||||
private Bitmap mOriginalBitmap = null;
|
||||
private RectF mOriginalBounds = null;
|
||||
private int mOriginalRotation = 0;
|
||||
private Uri mSourceUri = null;
|
||||
private CropView mCropView = null;
|
||||
private View mSaveButton = null;
|
||||
private boolean finalIOGuard = false;
|
||||
|
||||
private static final int SELECT_PICTURE = 1; // request code for picker
|
||||
|
||||
private static final int DEFAULT_COMPRESS_QUALITY = 90;
|
||||
/**
|
||||
* The maximum bitmap size we allow to be returned through the intent.
|
||||
* Intents have a maximum of 1MB in total size. However, the Bitmap seems to
|
||||
* have some overhead to hit so that we go way below the limit here to make
|
||||
* sure the intent stays below 1MB.We should consider just returning a byte
|
||||
* array instead of a Bitmap instance to avoid overhead.
|
||||
*/
|
||||
public static final int MAX_BMAP_IN_INTENT = 750000;
|
||||
|
||||
// Flags
|
||||
private static final int DO_SET_WALLPAPER = 1;
|
||||
private static final int DO_RETURN_DATA = 1 << 1;
|
||||
private static final int DO_EXTRA_OUTPUT = 1 << 2;
|
||||
|
||||
private static final int FLAG_CHECK = DO_SET_WALLPAPER | DO_RETURN_DATA | DO_EXTRA_OUTPUT;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Intent intent = getIntent();
|
||||
requestWindowFeature(Window.FEATURE_ACTION_BAR);
|
||||
setResult(RESULT_CANCELED, new Intent());
|
||||
mCropExtras = getExtrasFromIntent(intent);
|
||||
if (mCropExtras != null && mCropExtras.getShowWhenLocked()) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
}
|
||||
setContentView(R.layout.muna_activity_crop);
|
||||
mCropView = (CropView) findViewById(R.id.cropView);
|
||||
|
||||
ActionBar actionBar = getActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||
actionBar.setCustomView(R.layout.muna_filtershow_actionbar);
|
||||
|
||||
View mSaveButton = actionBar.getCustomView();
|
||||
mSaveButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mSourceUri == null) {
|
||||
setResult(RESULT_CANCELED, new Intent());
|
||||
done();
|
||||
} else {
|
||||
startFinishOutput();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (intent.getData() != null) {
|
||||
mSourceUri = intent.getData();
|
||||
startLoadBitmap(mSourceUri);
|
||||
} else {
|
||||
pickImage();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
}
|
||||
|
||||
private void enableSave(boolean enable) {
|
||||
if (mSaveButton != null) {
|
||||
mSaveButton.setEnabled(enable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (mLoadBitmapTask != null) {
|
||||
mLoadBitmapTask.cancel(false);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
mCropView.configChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a selector in Gallery to chose an image for use when none was given
|
||||
* in the CROP intent.
|
||||
*/
|
||||
private void pickImage() {
|
||||
Intent intent = new Intent();
|
||||
intent.setType("image/*");
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)),
|
||||
SELECT_PICTURE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for pickImage().
|
||||
*/
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode == RESULT_CANCELED && requestCode == SELECT_PICTURE) {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
|
||||
if (resultCode == RESULT_OK && requestCode == SELECT_PICTURE) {
|
||||
mSourceUri = data.getData();
|
||||
startLoadBitmap(mSourceUri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets screen size metric.
|
||||
*/
|
||||
private int getScreenImageSize() {
|
||||
DisplayMetrics outMetrics = new DisplayMetrics();
|
||||
getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
|
||||
return (int) Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that loads a bitmap in an async task.
|
||||
*/
|
||||
private void startLoadBitmap(Uri uri) {
|
||||
if (uri != null) {
|
||||
enableSave(false);
|
||||
final View loading = findViewById(R.id.loading);
|
||||
loading.setVisibility(View.VISIBLE);
|
||||
mLoadBitmapTask = new LoadBitmapTask();
|
||||
mLoadBitmapTask.execute(uri);
|
||||
} else {
|
||||
cannotLoadImage();
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called on UI thread with loaded bitmap.
|
||||
*/
|
||||
private void doneLoadBitmap(Bitmap bitmap, RectF bounds, int orientation) {
|
||||
final View loading = findViewById(R.id.loading);
|
||||
loading.setVisibility(View.GONE);
|
||||
mOriginalBitmap = bitmap;
|
||||
mOriginalBounds = bounds;
|
||||
mOriginalRotation = orientation;
|
||||
if (bitmap != null && bitmap.getWidth() != 0 && bitmap.getHeight() != 0) {
|
||||
RectF imgBounds = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
mCropView.initialize(bitmap, imgBounds, imgBounds, orientation);
|
||||
if (mCropExtras != null) {
|
||||
int aspectX = mCropExtras.getAspectX();
|
||||
int aspectY = mCropExtras.getAspectY();
|
||||
mOutputX = mCropExtras.getOutputX();
|
||||
mOutputY = mCropExtras.getOutputY();
|
||||
if (mOutputX > 0 && mOutputY > 0) {
|
||||
mCropView.applyAspect(mOutputX, mOutputY);
|
||||
|
||||
}
|
||||
float spotX = mCropExtras.getSpotlightX();
|
||||
float spotY = mCropExtras.getSpotlightY();
|
||||
if (spotX > 0 && spotY > 0) {
|
||||
mCropView.setWallpaperSpotlight(spotX, spotY);
|
||||
}
|
||||
if (aspectX > 0 && aspectY > 0) {
|
||||
mCropView.applyAspect(aspectX, aspectY);
|
||||
}
|
||||
}
|
||||
enableSave(true);
|
||||
} else {
|
||||
Log.w(TAG, "could not load image for cropping");
|
||||
cannotLoadImage();
|
||||
setResult(RESULT_CANCELED, new Intent());
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display toast for image loading failure.
|
||||
*/
|
||||
private void cannotLoadImage() {
|
||||
CharSequence text = getString(R.string.cannot_load_image);
|
||||
Toast toast = Toast.makeText(this, text, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* AsyncTask for loading a bitmap into memory.
|
||||
*
|
||||
* @see #startLoadBitmap(Uri)
|
||||
*/
|
||||
private class LoadBitmapTask extends AsyncTask<Uri, Void, Bitmap> {
|
||||
int mBitmapSize;
|
||||
Context mContext;
|
||||
Rect mOriginalBounds;
|
||||
int mOrientation;
|
||||
|
||||
public LoadBitmapTask() {
|
||||
mBitmapSize = getScreenImageSize();
|
||||
mContext = getApplicationContext();
|
||||
mOriginalBounds = new Rect();
|
||||
mOrientation = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap doInBackground(Uri... params) {
|
||||
Uri uri = params[0];
|
||||
Bitmap bmap = Utils.loadConstrainedBitmap(uri, mContext, mBitmapSize,
|
||||
mOriginalBounds, false);
|
||||
mOrientation = Utils.getMetadataRotation(mContext, uri);
|
||||
return bmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bitmap result) {
|
||||
doneLoadBitmap(result, new RectF(mOriginalBounds), mOrientation);
|
||||
}
|
||||
}
|
||||
|
||||
protected void startFinishOutput() {
|
||||
if (finalIOGuard) {
|
||||
return;
|
||||
} else {
|
||||
finalIOGuard = true;
|
||||
}
|
||||
enableSave(false);
|
||||
Uri destinationUri = null;
|
||||
int flags = 0;
|
||||
if (mOriginalBitmap != null && mCropExtras != null) {
|
||||
if (mCropExtras.getExtraOutput() != null) {
|
||||
destinationUri = mCropExtras.getExtraOutput();
|
||||
if (destinationUri != null) {
|
||||
flags |= DO_EXTRA_OUTPUT;
|
||||
}
|
||||
}
|
||||
if (mCropExtras.getSetAsWallpaper()) {
|
||||
flags |= DO_SET_WALLPAPER;
|
||||
}
|
||||
if (mCropExtras.getReturnData()) {
|
||||
flags |= DO_RETURN_DATA;
|
||||
}
|
||||
}
|
||||
if (flags == 0) {
|
||||
destinationUri = Utils.makeAndInsertUri(this, mSourceUri);
|
||||
if (destinationUri != null) {
|
||||
flags |= DO_EXTRA_OUTPUT;
|
||||
}
|
||||
}
|
||||
if ((flags & FLAG_CHECK) != 0 && mOriginalBitmap != null) {
|
||||
RectF photo = new RectF(0, 0, mOriginalBitmap.getWidth(), mOriginalBitmap.getHeight());
|
||||
RectF crop = getBitmapCrop(photo);
|
||||
startBitmapIO(flags, mOriginalBitmap, mSourceUri, destinationUri, crop,
|
||||
photo, mOriginalBounds,
|
||||
(mCropExtras == null) ? null : mCropExtras.getOutputFormat(), mOriginalRotation);
|
||||
return;
|
||||
}
|
||||
setResult(RESULT_CANCELED, new Intent());
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
private void startBitmapIO(int flags, Bitmap currentBitmap, Uri sourceUri, Uri destUri,
|
||||
RectF cropBounds, RectF photoBounds, RectF currentBitmapBounds, String format,
|
||||
int rotation) {
|
||||
if (cropBounds == null || photoBounds == null || currentBitmap == null
|
||||
|| currentBitmap.getWidth() == 0 || currentBitmap.getHeight() == 0
|
||||
|| cropBounds.width() == 0 || cropBounds.height() == 0 || photoBounds.width() == 0
|
||||
|| photoBounds.height() == 0) {
|
||||
return; // fail fast
|
||||
}
|
||||
if ((flags & FLAG_CHECK) == 0) {
|
||||
return; // no output options
|
||||
}
|
||||
if ((flags & DO_SET_WALLPAPER) != 0) {
|
||||
Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
final View loading = findViewById(R.id.loading);
|
||||
loading.setVisibility(View.VISIBLE);
|
||||
BitmapIOTask ioTask = new BitmapIOTask(sourceUri, destUri, format, flags, cropBounds,
|
||||
photoBounds, currentBitmapBounds, rotation, mOutputX, mOutputY);
|
||||
ioTask.execute(currentBitmap);
|
||||
}
|
||||
|
||||
private void doneBitmapIO(boolean success, Intent intent) {
|
||||
final View loading = findViewById(R.id.loading);
|
||||
loading.setVisibility(View.GONE);
|
||||
if (success) {
|
||||
setResult(RESULT_OK, intent);
|
||||
} else {
|
||||
setResult(RESULT_CANCELED, intent);
|
||||
}
|
||||
done();
|
||||
}
|
||||
|
||||
private class BitmapIOTask extends AsyncTask<Bitmap, Void, Boolean> {
|
||||
|
||||
private final WallpaperManager mWPManager;
|
||||
InputStream mInStream = null;
|
||||
OutputStream mOutStream = null;
|
||||
String mOutputFormat = null;
|
||||
Uri mOutUri = null;
|
||||
Uri mInUri = null;
|
||||
int mFlags = 0;
|
||||
RectF mCrop = null;
|
||||
RectF mPhoto = null;
|
||||
RectF mOrig = null;
|
||||
Intent mResultIntent = null;
|
||||
int mRotation = 0;
|
||||
|
||||
// Helper to setup input stream
|
||||
private void regenerateInputStream() {
|
||||
if (mInUri == null) {
|
||||
Log.w(TAG, "cannot read original file, no input URI given");
|
||||
} else {
|
||||
Utils.closeSilently(mInStream);
|
||||
try {
|
||||
mInStream = getContentResolver().openInputStream(mInUri);
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w(TAG, "cannot read file: " + mInUri.toString(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BitmapIOTask(Uri sourceUri, Uri destUri, String outputFormat, int flags,
|
||||
RectF cropBounds, RectF photoBounds, RectF originalBitmapBounds, int rotation,
|
||||
int outputX, int outputY) {
|
||||
mOutputFormat = outputFormat;
|
||||
mOutStream = null;
|
||||
mOutUri = destUri;
|
||||
mInUri = sourceUri;
|
||||
mFlags = flags;
|
||||
mCrop = cropBounds;
|
||||
mPhoto = photoBounds;
|
||||
mOrig = originalBitmapBounds;
|
||||
mWPManager = WallpaperManager.getInstance(getApplicationContext());
|
||||
mResultIntent = new Intent();
|
||||
mRotation = (rotation < 0) ? -rotation : rotation;
|
||||
mRotation %= 360;
|
||||
mRotation = 90 * (int) (mRotation / 90); // now mRotation is a multiple of 90
|
||||
mOutputX = outputX;
|
||||
mOutputY = outputY;
|
||||
|
||||
if ((flags & DO_EXTRA_OUTPUT) != 0) {
|
||||
if (mOutUri == null) {
|
||||
Log.w(TAG, "cannot write file, no output URI given");
|
||||
} else {
|
||||
try {
|
||||
mOutStream = getContentResolver().openOutputStream(mOutUri);
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w(TAG, "cannot write file: " + mOutUri.toString(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0) {
|
||||
regenerateInputStream();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Bitmap... params) {
|
||||
boolean failure = false;
|
||||
Bitmap img = params[0];
|
||||
try {
|
||||
// Set extra for crop bounds
|
||||
if (mCrop != null && mPhoto != null && mOrig != null) {
|
||||
RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig);
|
||||
Matrix m = new Matrix();
|
||||
m.setRotate(mRotation);
|
||||
m.mapRect(trueCrop);
|
||||
if (trueCrop != null) {
|
||||
Rect rounded = new Rect();
|
||||
trueCrop.roundOut(rounded);
|
||||
mResultIntent.putExtra(CropExtras.KEY_CROPPED_RECT, rounded);
|
||||
}
|
||||
}
|
||||
|
||||
// Find the small cropped bitmap that is returned in the intent
|
||||
if ((mFlags & DO_RETURN_DATA) != 0) {
|
||||
assert (img != null);
|
||||
Bitmap ret = getCroppedImage(img, mCrop, mPhoto);
|
||||
if (ret != null) {
|
||||
ret = getDownsampledBitmap(ret, MAX_BMAP_IN_INTENT);
|
||||
}
|
||||
if (ret == null) {
|
||||
Log.w(TAG, "could not downsample bitmap to return in data");
|
||||
failure = true;
|
||||
} else {
|
||||
if (mRotation > 0) {
|
||||
Matrix m = new Matrix();
|
||||
m.setRotate(mRotation);
|
||||
Bitmap tmp = Bitmap.createBitmap(ret, 0, 0, ret.getWidth(),
|
||||
ret.getHeight(), m, true);
|
||||
if (tmp != null) {
|
||||
ret = tmp;
|
||||
}
|
||||
}
|
||||
mResultIntent.putExtra(CropExtras.KEY_DATA, ret);
|
||||
}
|
||||
}
|
||||
|
||||
// Do the large cropped bitmap and/or set the wallpaper
|
||||
if ((mFlags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0 && mInStream != null) {
|
||||
// Find crop bounds (scaled to original image size)
|
||||
RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig);
|
||||
if (trueCrop == null) {
|
||||
Log.w(TAG, "cannot find crop for full size image");
|
||||
failure = true;
|
||||
return false;
|
||||
}
|
||||
Rect roundedTrueCrop = new Rect();
|
||||
trueCrop.roundOut(roundedTrueCrop);
|
||||
|
||||
if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
|
||||
Log.w(TAG, "crop has bad values for full size image");
|
||||
failure = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to open a region decoder
|
||||
BitmapRegionDecoder decoder = null;
|
||||
try {
|
||||
decoder = BitmapRegionDecoder.newInstance(mInStream, true);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "cannot open region decoder for file: " + mInUri.toString(), e);
|
||||
}
|
||||
|
||||
Bitmap crop = null;
|
||||
if (decoder != null) {
|
||||
// Do region decoding to get crop bitmap
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inMutable = true;
|
||||
crop = decoder.decodeRegion(roundedTrueCrop, options);
|
||||
decoder.recycle();
|
||||
}
|
||||
|
||||
if (crop == null) {
|
||||
// BitmapRegionDecoder has failed, try to crop in-memory
|
||||
regenerateInputStream();
|
||||
Bitmap fullSize = null;
|
||||
if (mInStream != null) {
|
||||
fullSize = BitmapFactory.decodeStream(mInStream);
|
||||
}
|
||||
if (fullSize != null) {
|
||||
crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
|
||||
roundedTrueCrop.top, roundedTrueCrop.width(),
|
||||
roundedTrueCrop.height());
|
||||
}
|
||||
}
|
||||
|
||||
if (crop == null) {
|
||||
Log.w(TAG, "cannot decode file: " + mInUri.toString());
|
||||
failure = true;
|
||||
return false;
|
||||
}
|
||||
if (mOutputX > 0 && mOutputY > 0) {
|
||||
Matrix m = new Matrix();
|
||||
RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight());
|
||||
if (mRotation > 0) {
|
||||
m.setRotate(mRotation);
|
||||
m.mapRect(cropRect);
|
||||
}
|
||||
RectF returnRect = new RectF(0, 0, mOutputX, mOutputY);
|
||||
m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
|
||||
m.preRotate(mRotation);
|
||||
Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
|
||||
(int) returnRect.height(), Bitmap.Config.ARGB_8888);
|
||||
if (tmp != null) {
|
||||
Canvas c = new Canvas(tmp);
|
||||
c.drawBitmap(crop, m, new Paint());
|
||||
crop = tmp;
|
||||
}
|
||||
} else if (mRotation > 0) {
|
||||
Matrix m = new Matrix();
|
||||
m.setRotate(mRotation);
|
||||
Bitmap tmp = Bitmap.createBitmap(crop, 0, 0, crop.getWidth(),
|
||||
crop.getHeight(), m, true);
|
||||
if (tmp != null) {
|
||||
crop = tmp;
|
||||
}
|
||||
}
|
||||
// Get output compression format
|
||||
CompressFormat cf =
|
||||
convertExtensionToCompressFormat(getFileExtension(mOutputFormat));
|
||||
|
||||
// If we only need to output to a URI, compress straight to file
|
||||
if (mFlags == DO_EXTRA_OUTPUT) {
|
||||
if (mOutStream == null
|
||||
|| !crop.compress(cf, DEFAULT_COMPRESS_QUALITY, mOutStream)) {
|
||||
Log.w(TAG, "failed to compress bitmap to file: " + mOutUri.toString());
|
||||
failure = true;
|
||||
} else {
|
||||
mResultIntent.setData(mOutUri);
|
||||
}
|
||||
} else {
|
||||
// Compress to byte array
|
||||
ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
|
||||
if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
|
||||
|
||||
// If we need to output to a Uri, write compressed
|
||||
// bitmap out
|
||||
if ((mFlags & DO_EXTRA_OUTPUT) != 0) {
|
||||
if (mOutStream == null) {
|
||||
Log.w(TAG,
|
||||
"failed to compress bitmap to file: " + mOutUri.toString());
|
||||
failure = true;
|
||||
} else {
|
||||
try {
|
||||
mOutStream.write(tmpOut.toByteArray());
|
||||
mResultIntent.setData(mOutUri);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG,
|
||||
"failed to compress bitmap to file: "
|
||||
+ mOutUri.toString(), e);
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we need to set to the wallpaper, set it
|
||||
if ((mFlags & DO_SET_WALLPAPER) != 0 && mWPManager != null) {
|
||||
if (mWPManager == null) {
|
||||
Log.w(TAG, "no wallpaper manager");
|
||||
failure = true;
|
||||
} else {
|
||||
try {
|
||||
mWPManager.setStream(new ByteArrayInputStream(tmpOut
|
||||
.toByteArray()));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "cannot write stream to wallpaper", e);
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "cannot compress bitmap");
|
||||
failure = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
failure = true;
|
||||
}
|
||||
return !failure; // True if any of the operations failed
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
Utils.closeSilently(mOutStream);
|
||||
Utils.closeSilently(mInStream);
|
||||
doneBitmapIO(result.booleanValue(), mResultIntent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void done() {
|
||||
finish();
|
||||
}
|
||||
|
||||
protected static Bitmap getCroppedImage(Bitmap image, RectF cropBounds, RectF photoBounds) {
|
||||
RectF imageBounds = new RectF(0, 0, image.getWidth(), image.getHeight());
|
||||
RectF crop = CropMath.getScaledCropBounds(cropBounds, photoBounds, imageBounds);
|
||||
if (crop == null) {
|
||||
return null;
|
||||
}
|
||||
Rect intCrop = new Rect();
|
||||
crop.roundOut(intCrop);
|
||||
return Bitmap.createBitmap(image, intCrop.left, intCrop.top, intCrop.width(),
|
||||
intCrop.height());
|
||||
}
|
||||
|
||||
protected static Bitmap getDownsampledBitmap(Bitmap image, int max_size) {
|
||||
if (image == null || image.getWidth() == 0 || image.getHeight() == 0 || max_size < 16) {
|
||||
throw new IllegalArgumentException("Bad argument to getDownsampledBitmap()");
|
||||
}
|
||||
int shifts = 0;
|
||||
int size = CropMath.getBitmapSize(image);
|
||||
while (size > max_size) {
|
||||
shifts++;
|
||||
size /= 4;
|
||||
}
|
||||
Bitmap ret = Bitmap.createScaledBitmap(image, image.getWidth() >> shifts,
|
||||
image.getHeight() >> shifts, true);
|
||||
if (ret == null) {
|
||||
return null;
|
||||
}
|
||||
// Handle edge case for rounding.
|
||||
if (CropMath.getBitmapSize(ret) > max_size) {
|
||||
return Bitmap.createScaledBitmap(ret, ret.getWidth() >> 1, ret.getHeight() >> 1, true);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the crop extras from the intent, or null if none exist.
|
||||
*/
|
||||
protected static CropExtras getExtrasFromIntent(Intent intent) {
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras != null) {
|
||||
return new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0),
|
||||
extras.getInt(CropExtras.KEY_OUTPUT_Y, 0),
|
||||
extras.getBoolean(CropExtras.KEY_SCALE, true) &&
|
||||
extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false),
|
||||
extras.getInt(CropExtras.KEY_ASPECT_X, 0),
|
||||
extras.getInt(CropExtras.KEY_ASPECT_Y, 0),
|
||||
extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false),
|
||||
extras.getBoolean(CropExtras.KEY_RETURN_DATA, false),
|
||||
(Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT),
|
||||
extras.getString(CropExtras.KEY_OUTPUT_FORMAT),
|
||||
extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false),
|
||||
extras.getFloat(CropExtras.KEY_SPOTLIGHT_X),
|
||||
extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static CompressFormat convertExtensionToCompressFormat(String extension) {
|
||||
return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
|
||||
}
|
||||
|
||||
protected static String getFileExtension(String requestFormat) {
|
||||
String outputFormat = (requestFormat == null)
|
||||
? "jpg"
|
||||
: requestFormat;
|
||||
outputFormat = outputFormat.toLowerCase();
|
||||
return (outputFormat.equals("png") || outputFormat.equals("gif"))
|
||||
? "png" // We don't support gif compression.
|
||||
: "jpg";
|
||||
}
|
||||
|
||||
private RectF getBitmapCrop(RectF imageBounds) {
|
||||
RectF crop = mCropView.getCrop();
|
||||
RectF photo = mCropView.getPhoto();
|
||||
if (crop == null || photo == null) {
|
||||
Log.w(TAG, "could not get crop");
|
||||
return null;
|
||||
}
|
||||
RectF scaledCrop = CropMath.getScaledCropBounds(crop, photo, imageBounds);
|
||||
return scaledCrop;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.fengliyan.uikit.crop.app;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
public abstract class CropDrawingUtils {
|
||||
|
||||
public static void drawRuleOfThird(Canvas canvas, RectF bounds) {
|
||||
Paint p = new Paint();
|
||||
p.setStyle(Paint.Style.STROKE);
|
||||
p.setColor(Color.argb(128, 255, 255, 255));
|
||||
p.setStrokeWidth(2);
|
||||
float stepX = bounds.width() / 3.0f;
|
||||
float stepY = bounds.height() / 3.0f;
|
||||
float x = bounds.left + stepX;
|
||||
float y = bounds.top + stepY;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
canvas.drawLine(x, bounds.top, x, bounds.bottom, p);
|
||||
x += stepX;
|
||||
}
|
||||
for (int j = 0; j < 2; j++) {
|
||||
canvas.drawLine(bounds.left, y, bounds.right, y, p);
|
||||
y += stepY;
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawCropRect(Canvas canvas, RectF bounds) {
|
||||
Paint p = new Paint();
|
||||
p.setStyle(Paint.Style.STROKE);
|
||||
p.setColor(Color.WHITE);
|
||||
p.setStrokeWidth(3);
|
||||
canvas.drawRect(bounds, p);
|
||||
}
|
||||
|
||||
public static void drawShade(Canvas canvas, RectF bounds) {
|
||||
int w = canvas.getWidth();
|
||||
int h = canvas.getHeight();
|
||||
Paint p = new Paint();
|
||||
p.setStyle(Paint.Style.FILL);
|
||||
p.setColor(Color.BLACK & 0x88000000);
|
||||
|
||||
RectF r = new RectF();
|
||||
r.set(0,0,w,bounds.top);
|
||||
canvas.drawRect(r, p);
|
||||
r.set(0,bounds.top,bounds.left,h);
|
||||
canvas.drawRect(r, p);
|
||||
r.set(bounds.left,bounds.bottom,w,h);
|
||||
canvas.drawRect(r, p);
|
||||
r.set(bounds.right,bounds.top,w,bounds.bottom);
|
||||
canvas.drawRect(r, p);
|
||||
}
|
||||
|
||||
public static void drawIndicator(Canvas canvas, Drawable indicator, int indicatorSize,
|
||||
float centerX, float centerY) {
|
||||
int left = (int) centerX - indicatorSize / 2;
|
||||
int top = (int) centerY - indicatorSize / 2;
|
||||
indicator.setBounds(left, top, left + indicatorSize, top + indicatorSize);
|
||||
indicator.draw(canvas);
|
||||
}
|
||||
|
||||
public static void drawIndicators(Canvas canvas, Drawable cropIndicator, int indicatorSize,
|
||||
RectF bounds, boolean fixedAspect, int selection) {
|
||||
boolean notMoving = (selection == CropObject.MOVE_NONE);
|
||||
if (fixedAspect) {
|
||||
if ((selection == CropObject.TOP_LEFT) || notMoving) {
|
||||
drawIndicator(canvas, cropIndicator, indicatorSize, bounds.left, bounds.top);
|
||||
}
|
||||
if ((selection == CropObject.TOP_RIGHT) || notMoving) {
|
||||
drawIndicator(canvas, cropIndicator, indicatorSize, bounds.right, bounds.top);
|
||||
}
|
||||
if ((selection == CropObject.BOTTOM_LEFT) || notMoving) {
|
||||
drawIndicator(canvas, cropIndicator, indicatorSize, bounds.left, bounds.bottom);
|
||||
}
|
||||
if ((selection == CropObject.BOTTOM_RIGHT) || notMoving) {
|
||||
drawIndicator(canvas, cropIndicator, indicatorSize, bounds.right, bounds.bottom);
|
||||
}
|
||||
} else {
|
||||
if (((selection & CropObject.MOVE_TOP) != 0) || notMoving) {
|
||||
drawIndicator(canvas, cropIndicator, indicatorSize, bounds.centerX(), bounds.top);
|
||||
}
|
||||
if (((selection & CropObject.MOVE_BOTTOM) != 0) || notMoving) {
|
||||
drawIndicator(canvas, cropIndicator, indicatorSize, bounds.centerX(), bounds.bottom);
|
||||
}
|
||||
if (((selection & CropObject.MOVE_LEFT) != 0) || notMoving) {
|
||||
drawIndicator(canvas, cropIndicator, indicatorSize, bounds.left, bounds.centerY());
|
||||
}
|
||||
if (((selection & CropObject.MOVE_RIGHT) != 0) || notMoving) {
|
||||
drawIndicator(canvas, cropIndicator, indicatorSize, bounds.right, bounds.centerY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawWallpaperSelectionFrame(Canvas canvas, RectF cropBounds, float spotX,
|
||||
float spotY, Paint p, Paint shadowPaint) {
|
||||
float sx = cropBounds.width() * spotX;
|
||||
float sy = cropBounds.height() * spotY;
|
||||
float cx = cropBounds.centerX();
|
||||
float cy = cropBounds.centerY();
|
||||
RectF r1 = new RectF(cx - sx / 2, cy - sy / 2, cx + sx / 2, cy + sy / 2);
|
||||
float temp = sx;
|
||||
sx = sy;
|
||||
sy = temp;
|
||||
RectF r2 = new RectF(cx - sx / 2, cy - sy / 2, cx + sx / 2, cy + sy / 2);
|
||||
canvas.save();
|
||||
canvas.clipRect(cropBounds);
|
||||
canvas.clipRect(r1, Region.Op.DIFFERENCE);
|
||||
canvas.clipRect(r2, Region.Op.DIFFERENCE);
|
||||
canvas.drawPaint(shadowPaint);
|
||||
canvas.restore();
|
||||
Path path = new Path();
|
||||
path.moveTo(r1.left, r1.top);
|
||||
path.lineTo(r1.right, r1.top);
|
||||
path.moveTo(r1.left, r1.top);
|
||||
path.lineTo(r1.left, r1.bottom);
|
||||
path.moveTo(r1.left, r1.bottom);
|
||||
path.lineTo(r1.right, r1.bottom);
|
||||
path.moveTo(r1.right, r1.top);
|
||||
path.lineTo(r1.right, r1.bottom);
|
||||
path.moveTo(r2.left, r2.top);
|
||||
path.lineTo(r2.right, r2.top);
|
||||
path.moveTo(r2.right, r2.top);
|
||||
path.lineTo(r2.right, r2.bottom);
|
||||
path.moveTo(r2.left, r2.bottom);
|
||||
path.lineTo(r2.right, r2.bottom);
|
||||
path.moveTo(r2.left, r2.top);
|
||||
path.lineTo(r2.left, r2.bottom);
|
||||
canvas.drawPath(path, p);
|
||||
}
|
||||
|
||||
public static void drawShadows(Canvas canvas, Paint p, RectF innerBounds, RectF outerBounds) {
|
||||
canvas.drawRect(outerBounds.left, outerBounds.top, innerBounds.right, innerBounds.top, p);
|
||||
canvas.drawRect(innerBounds.right, outerBounds.top, outerBounds.right, innerBounds.bottom,
|
||||
p);
|
||||
canvas.drawRect(innerBounds.left, innerBounds.bottom, outerBounds.right,
|
||||
outerBounds.bottom, p);
|
||||
canvas.drawRect(outerBounds.left, innerBounds.top, innerBounds.left, outerBounds.bottom, p);
|
||||
}
|
||||
|
||||
public static Matrix getBitmapToDisplayMatrix(RectF imageBounds, RectF displayBounds) {
|
||||
Matrix m = new Matrix();
|
||||
CropDrawingUtils.setBitmapToDisplayMatrix(m, imageBounds, displayBounds);
|
||||
return m;
|
||||
}
|
||||
|
||||
public static boolean setBitmapToDisplayMatrix(Matrix m, RectF imageBounds,
|
||||
RectF displayBounds) {
|
||||
m.reset();
|
||||
return m.setRectToRect(imageBounds, displayBounds, Matrix.ScaleToFit.CENTER);
|
||||
}
|
||||
|
||||
public static boolean setImageToScreenMatrix(Matrix dst, RectF image,
|
||||
RectF screen, int rotation) {
|
||||
RectF rotatedImage = new RectF();
|
||||
dst.setRotate(rotation, image.centerX(), image.centerY());
|
||||
if (!dst.mapRect(rotatedImage, image)) {
|
||||
return false; // fails for rotations that are not multiples of 90
|
||||
// degrees
|
||||
}
|
||||
boolean rToR = dst.setRectToRect(rotatedImage, screen, Matrix.ScaleToFit.CENTER);
|
||||
boolean rot = dst.preRotate(rotation, image.centerX(), image.centerY());
|
||||
return rToR && rot;
|
||||
}
|
||||
|
||||
}
|
||||
121
uikit/src/main/java/com/fengliyan/uikit/crop/app/CropExtras.java
Normal file
121
uikit/src/main/java/com/fengliyan/uikit/crop/app/CropExtras.java
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.fengliyan.uikit.crop.app;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
public class CropExtras {
|
||||
|
||||
public static final String KEY_CROPPED_RECT = "cropped-rect";
|
||||
public static final String KEY_OUTPUT_X = "outputX";
|
||||
public static final String KEY_OUTPUT_Y = "outputY";
|
||||
public static final String KEY_SCALE = "scale";
|
||||
public static final String KEY_SCALE_UP_IF_NEEDED = "scaleUpIfNeeded";
|
||||
public static final String KEY_ASPECT_X = "aspectX";
|
||||
public static final String KEY_ASPECT_Y = "aspectY";
|
||||
public static final String KEY_SET_AS_WALLPAPER = "set-as-wallpaper";
|
||||
public static final String KEY_RETURN_DATA = "return-data";
|
||||
public static final String KEY_DATA = "data";
|
||||
public static final String KEY_SPOTLIGHT_X = "spotlightX";
|
||||
public static final String KEY_SPOTLIGHT_Y = "spotlightY";
|
||||
public static final String KEY_SHOW_WHEN_LOCKED = "showWhenLocked";
|
||||
public static final String KEY_OUTPUT_FORMAT = "outputFormat";
|
||||
|
||||
private int mOutputX = 0;
|
||||
private int mOutputY = 0;
|
||||
private boolean mScaleUp = true;
|
||||
private int mAspectX = 0;
|
||||
private int mAspectY = 0;
|
||||
private boolean mSetAsWallpaper = false;
|
||||
private boolean mReturnData = false;
|
||||
private Uri mExtraOutput = null;
|
||||
private String mOutputFormat = null;
|
||||
private boolean mShowWhenLocked = false;
|
||||
private float mSpotlightX = 0;
|
||||
private float mSpotlightY = 0;
|
||||
|
||||
public CropExtras(int outputX, int outputY, boolean scaleUp, int aspectX, int aspectY,
|
||||
boolean setAsWallpaper, boolean returnData, Uri extraOutput, String outputFormat,
|
||||
boolean showWhenLocked, float spotlightX, float spotlightY) {
|
||||
mOutputX = outputX;
|
||||
mOutputY = outputY;
|
||||
mScaleUp = scaleUp;
|
||||
mAspectX = aspectX;
|
||||
mAspectY = aspectY;
|
||||
mSetAsWallpaper = setAsWallpaper;
|
||||
mReturnData = returnData;
|
||||
mExtraOutput = extraOutput;
|
||||
mOutputFormat = outputFormat;
|
||||
mShowWhenLocked = showWhenLocked;
|
||||
mSpotlightX = spotlightX;
|
||||
mSpotlightY = spotlightY;
|
||||
}
|
||||
|
||||
public CropExtras(CropExtras c) {
|
||||
this(c.mOutputX, c.mOutputY, c.mScaleUp, c.mAspectX, c.mAspectY, c.mSetAsWallpaper,
|
||||
c.mReturnData, c.mExtraOutput, c.mOutputFormat, c.mShowWhenLocked,
|
||||
c.mSpotlightX, c.mSpotlightY);
|
||||
}
|
||||
|
||||
public int getOutputX() {
|
||||
return mOutputX;
|
||||
}
|
||||
|
||||
public int getOutputY() {
|
||||
return mOutputY;
|
||||
}
|
||||
|
||||
public boolean getScaleUp() {
|
||||
return mScaleUp;
|
||||
}
|
||||
|
||||
public int getAspectX() {
|
||||
return mAspectX;
|
||||
}
|
||||
|
||||
public int getAspectY() {
|
||||
return mAspectY;
|
||||
}
|
||||
|
||||
public boolean getSetAsWallpaper() {
|
||||
return mSetAsWallpaper;
|
||||
}
|
||||
|
||||
public boolean getReturnData() {
|
||||
return mReturnData;
|
||||
}
|
||||
|
||||
public Uri getExtraOutput() {
|
||||
return mExtraOutput;
|
||||
}
|
||||
|
||||
public String getOutputFormat() {
|
||||
return mOutputFormat;
|
||||
}
|
||||
|
||||
public boolean getShowWhenLocked() {
|
||||
return mShowWhenLocked;
|
||||
}
|
||||
|
||||
public float getSpotlightX() {
|
||||
return mSpotlightX;
|
||||
}
|
||||
|
||||
public float getSpotlightY() {
|
||||
return mSpotlightY;
|
||||
}
|
||||
}
|
||||
258
uikit/src/main/java/com/fengliyan/uikit/crop/app/CropMath.java
Normal file
258
uikit/src/main/java/com/fengliyan/uikit/crop/app/CropMath.java
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.fengliyan.uikit.crop.app;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CropMath {
|
||||
|
||||
/**
|
||||
* Gets a float array of the 2D coordinates representing a rectangles
|
||||
* corners.
|
||||
* The order of the corners in the float array is:
|
||||
* 0------->1
|
||||
* ^ |
|
||||
* | v
|
||||
* 3<-------2
|
||||
*
|
||||
* @param r the rectangle to get the corners of
|
||||
* @return the float array of corners (8 floats)
|
||||
*/
|
||||
|
||||
public static float[] getCornersFromRect(RectF r) {
|
||||
float[] corners = {
|
||||
r.left, r.top,
|
||||
r.right, r.top,
|
||||
r.right, r.bottom,
|
||||
r.left, r.bottom
|
||||
};
|
||||
return corners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true iff point (x, y) is within or on the rectangle's bounds.
|
||||
* RectF's "contains" function treats points on the bottom and right bound
|
||||
* as not being contained.
|
||||
*
|
||||
* @param r the rectangle
|
||||
* @param x the x value of the point
|
||||
* @param y the y value of the point
|
||||
* @return
|
||||
*/
|
||||
public static boolean inclusiveContains(RectF r, float x, float y) {
|
||||
return !(x > r.right || x < r.left || y > r.bottom || y < r.top);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of 2D coordinates representing corners and returns the
|
||||
* smallest rectangle containing those coordinates.
|
||||
*
|
||||
* @param array array of 2D coordinates
|
||||
* @return smallest rectangle containing coordinates
|
||||
*/
|
||||
public static RectF trapToRect(float[] array) {
|
||||
RectF r = new RectF(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
|
||||
Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
|
||||
for (int i = 1; i < array.length; i += 2) {
|
||||
float x = array[i - 1];
|
||||
float y = array[i];
|
||||
r.left = (x < r.left) ? x : r.left;
|
||||
r.top = (y < r.top) ? y : r.top;
|
||||
r.right = (x > r.right) ? x : r.right;
|
||||
r.bottom = (y > r.bottom) ? y : r.bottom;
|
||||
}
|
||||
r.sort();
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the
|
||||
* image bound rectangle, clamps it to the edge of the rectangle.
|
||||
*
|
||||
* @param imageBound the rectangle to clamp edge points to.
|
||||
* @param array an array of points to clamp to the rectangle, gets set to
|
||||
* the clamped values.
|
||||
*/
|
||||
public static void getEdgePoints(RectF imageBound, float[] array) {
|
||||
if (array.length < 2)
|
||||
return;
|
||||
for (int x = 0; x < array.length; x += 2) {
|
||||
array[x] = Utils.clamp(array[x], imageBound.left, imageBound.right);
|
||||
array[x + 1] = Utils.clamp(array[x + 1], imageBound.top, imageBound.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a point and the corners of a rectangle and returns the two corners
|
||||
* representing the side of the rectangle closest to the point.
|
||||
*
|
||||
* @param point the point which is being checked
|
||||
* @param corners the corners of the rectangle
|
||||
* @return two corners representing the side of the rectangle
|
||||
*/
|
||||
public static float[] closestSide(float[] point, float[] corners) {
|
||||
int len = corners.length;
|
||||
float oldMag = Float.POSITIVE_INFINITY;
|
||||
float[] bestLine = null;
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
float[] line = {
|
||||
corners[i], corners[(i + 1) % len],
|
||||
corners[(i + 2) % len], corners[(i + 3) % len]
|
||||
};
|
||||
float mag = Utils.vectorLength(
|
||||
Utils.shortestVectorFromPointToLine(point, line));
|
||||
if (mag < oldMag) {
|
||||
oldMag = mag;
|
||||
bestLine = line;
|
||||
}
|
||||
}
|
||||
return bestLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given point is within a rotated rectangle.
|
||||
*
|
||||
* @param point 2D point to check
|
||||
* @param bound rectangle to rotate
|
||||
* @param rot angle of rotation about rectangle center
|
||||
* @return true if point is within rotated rectangle
|
||||
*/
|
||||
public static boolean pointInRotatedRect(float[] point, RectF bound, float rot) {
|
||||
Matrix m = new Matrix();
|
||||
float[] p = Arrays.copyOf(point, 2);
|
||||
m.setRotate(rot, bound.centerX(), bound.centerY());
|
||||
Matrix m0 = new Matrix();
|
||||
if (!m.invert(m0))
|
||||
return false;
|
||||
m0.mapPoints(p);
|
||||
return inclusiveContains(bound, p[0], p[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given point is within a rotated rectangle.
|
||||
*
|
||||
* @param point 2D point to check
|
||||
* @param rotatedRect corners of a rotated rectangle
|
||||
* @param center center of the rotated rectangle
|
||||
* @return true if point is within rotated rectangle
|
||||
*/
|
||||
public static boolean pointInRotatedRect(float[] point, float[] rotatedRect, float[] center) {
|
||||
RectF unrotated = new RectF();
|
||||
float angle = getUnrotated(rotatedRect, center, unrotated);
|
||||
return pointInRotatedRect(point, unrotated, angle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes rectangle to have a certain aspect ratio (center remains
|
||||
* stationary).
|
||||
*
|
||||
* @param r rectangle to resize
|
||||
* @param w new width aspect
|
||||
* @param h new height aspect
|
||||
*/
|
||||
public static void fixAspectRatio(RectF r, float w, float h) {
|
||||
float scale = Math.min(r.width() / w, r.height() / h);
|
||||
float centX = r.centerX();
|
||||
float centY = r.centerY();
|
||||
float hw = scale * w / 2;
|
||||
float hh = scale * h / 2;
|
||||
r.set(centX - hw, centY - hh, centX + hw, centY + hh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes rectangle to have a certain aspect ratio (center remains
|
||||
* stationary) while constraining it to remain within the original rect.
|
||||
*
|
||||
* @param r rectangle to resize
|
||||
* @param w new width aspect
|
||||
* @param h new height aspect
|
||||
*/
|
||||
public static void fixAspectRatioContained(RectF r, float w, float h) {
|
||||
float origW = r.width();
|
||||
float origH = r.height();
|
||||
float origA = origW / origH;
|
||||
float a = w / h;
|
||||
float finalW = origW;
|
||||
float finalH = origH;
|
||||
if (origA < a) {
|
||||
finalH = origW / a;
|
||||
r.top = r.centerY() - finalH / 2;
|
||||
r.bottom = r.top + finalH;
|
||||
} else {
|
||||
finalW = origH * a;
|
||||
r.left = r.centerX() - finalW / 2;
|
||||
r.right = r.left + finalW;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stretches/Scales/Translates photoBounds to match displayBounds, and
|
||||
* and returns an equivalent stretched/scaled/translated cropBounds or null
|
||||
* if the mapping is invalid.
|
||||
* @param cropBounds cropBounds to transform
|
||||
* @param photoBounds original bounds containing crop bounds
|
||||
* @param displayBounds final bounds for crop
|
||||
* @return the stretched/scaled/translated crop bounds that fit within displayBounds
|
||||
*/
|
||||
public static RectF getScaledCropBounds(RectF cropBounds, RectF photoBounds,
|
||||
RectF displayBounds) {
|
||||
Matrix m = new Matrix();
|
||||
m.setRectToRect(photoBounds, displayBounds, Matrix.ScaleToFit.FILL);
|
||||
RectF trueCrop = new RectF(cropBounds);
|
||||
if (!m.mapRect(trueCrop)) {
|
||||
return null;
|
||||
}
|
||||
return trueCrop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of a bitmap in bytes.
|
||||
* @param bmap bitmap whose size to check
|
||||
* @return bitmap size in bytes
|
||||
*/
|
||||
public static int getBitmapSize(Bitmap bmap) {
|
||||
return bmap.getRowBytes() * bmap.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constrains rotation to be in [0, 90, 180, 270] rounding down.
|
||||
* @param rotation any rotation value, in degrees
|
||||
* @return integer rotation in [0, 90, 180, 270]
|
||||
*/
|
||||
public static int constrainedRotation(float rotation) {
|
||||
int r = (int) ((rotation % 360) / 90);
|
||||
r = (r < 0) ? (r + 4) : r;
|
||||
return r * 90;
|
||||
}
|
||||
|
||||
private static float getUnrotated(float[] rotatedRect, float[] center, RectF unrotated) {
|
||||
float dy = rotatedRect[1] - rotatedRect[3];
|
||||
float dx = rotatedRect[0] - rotatedRect[2];
|
||||
float angle = (float) (Math.atan(dy / dx) * 180 / Math.PI);
|
||||
Matrix m = new Matrix();
|
||||
m.setRotate(-angle, center[0], center[1]);
|
||||
float[] unrotatedRect = new float[rotatedRect.length];
|
||||
m.mapPoints(unrotatedRect, rotatedRect);
|
||||
unrotated.set(trapToRect(unrotatedRect));
|
||||
return angle;
|
||||
}
|
||||
|
||||
}
|
||||
328
uikit/src/main/java/com/fengliyan/uikit/crop/app/CropObject.java
Normal file
328
uikit/src/main/java/com/fengliyan/uikit/crop/app/CropObject.java
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.fengliyan.uikit.crop.app;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
|
||||
public class CropObject {
|
||||
private BoundedRect mBoundedRect;
|
||||
private float mAspectWidth = 1;
|
||||
private float mAspectHeight = 1;
|
||||
private boolean mFixAspectRatio = false;
|
||||
private float mRotation = 0;
|
||||
private float mTouchTolerance = 45;
|
||||
private float mMinSideSize = 20;
|
||||
|
||||
public static final int MOVE_NONE = 0;
|
||||
// Sides
|
||||
public static final int MOVE_LEFT = 1;
|
||||
public static final int MOVE_TOP = 2;
|
||||
public static final int MOVE_RIGHT = 4;
|
||||
public static final int MOVE_BOTTOM = 8;
|
||||
public static final int MOVE_BLOCK = 16;
|
||||
|
||||
// Corners
|
||||
public static final int TOP_LEFT = MOVE_TOP | MOVE_LEFT;
|
||||
public static final int TOP_RIGHT = MOVE_TOP | MOVE_RIGHT;
|
||||
public static final int BOTTOM_RIGHT = MOVE_BOTTOM | MOVE_RIGHT;
|
||||
public static final int BOTTOM_LEFT = MOVE_BOTTOM | MOVE_LEFT;
|
||||
|
||||
private int mMovingEdges = MOVE_NONE;
|
||||
|
||||
public CropObject(Rect outerBound, Rect innerBound, int outerAngle) {
|
||||
mBoundedRect = new BoundedRect(outerAngle % 360, outerBound, innerBound);
|
||||
}
|
||||
|
||||
public CropObject(RectF outerBound, RectF innerBound, int outerAngle) {
|
||||
mBoundedRect = new BoundedRect(outerAngle % 360, outerBound, innerBound);
|
||||
}
|
||||
|
||||
public void resetBoundsTo(RectF inner, RectF outer) {
|
||||
mBoundedRect.resetTo(0, outer, inner);
|
||||
}
|
||||
|
||||
public void getInnerBounds(RectF r) {
|
||||
mBoundedRect.setToInner(r);
|
||||
}
|
||||
|
||||
public void getOuterBounds(RectF r) {
|
||||
mBoundedRect.setToOuter(r);
|
||||
}
|
||||
|
||||
public RectF getInnerBounds() {
|
||||
return mBoundedRect.getInner();
|
||||
}
|
||||
|
||||
public RectF getOuterBounds() {
|
||||
return mBoundedRect.getOuter();
|
||||
}
|
||||
|
||||
public int getSelectState() {
|
||||
return mMovingEdges;
|
||||
}
|
||||
|
||||
public boolean isFixedAspect() {
|
||||
return mFixAspectRatio;
|
||||
}
|
||||
|
||||
public void rotateOuter(int angle) {
|
||||
mRotation = angle % 360;
|
||||
mBoundedRect.setRotation(mRotation);
|
||||
clearSelectState();
|
||||
}
|
||||
|
||||
public boolean setInnerAspectRatio(float width, float height) {
|
||||
if (width <= 0 || height <= 0) {
|
||||
throw new IllegalArgumentException("Width and Height must be greater than zero");
|
||||
}
|
||||
RectF inner = mBoundedRect.getInner();
|
||||
CropMath.fixAspectRatioContained(inner, width, height);
|
||||
if (inner.width() < mMinSideSize || inner.height() < mMinSideSize) {
|
||||
return false;
|
||||
}
|
||||
mAspectWidth = width;
|
||||
mAspectHeight = height;
|
||||
mFixAspectRatio = true;
|
||||
mBoundedRect.setInner(inner);
|
||||
clearSelectState();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setTouchTolerance(float tolerance) {
|
||||
if (tolerance <= 0) {
|
||||
throw new IllegalArgumentException("Tolerance must be greater than zero");
|
||||
}
|
||||
mTouchTolerance = tolerance;
|
||||
}
|
||||
|
||||
public void setMinInnerSideSize(float minSide) {
|
||||
if (minSide <= 0) {
|
||||
throw new IllegalArgumentException("Min dide must be greater than zero");
|
||||
}
|
||||
mMinSideSize = minSide;
|
||||
}
|
||||
|
||||
public void unsetAspectRatio() {
|
||||
mFixAspectRatio = false;
|
||||
clearSelectState();
|
||||
}
|
||||
|
||||
public boolean hasSelectedEdge() {
|
||||
return mMovingEdges != MOVE_NONE;
|
||||
}
|
||||
|
||||
public static boolean checkCorner(int selected) {
|
||||
return selected == TOP_LEFT || selected == TOP_RIGHT || selected == BOTTOM_RIGHT
|
||||
|| selected == BOTTOM_LEFT;
|
||||
}
|
||||
|
||||
public static boolean checkEdge(int selected) {
|
||||
return selected == MOVE_LEFT || selected == MOVE_TOP || selected == MOVE_RIGHT
|
||||
|| selected == MOVE_BOTTOM;
|
||||
}
|
||||
|
||||
public static boolean checkBlock(int selected) {
|
||||
return selected == MOVE_BLOCK;
|
||||
}
|
||||
|
||||
public static boolean checkValid(int selected) {
|
||||
return selected == MOVE_NONE || checkBlock(selected) || checkEdge(selected)
|
||||
|| checkCorner(selected);
|
||||
}
|
||||
|
||||
public void clearSelectState() {
|
||||
mMovingEdges = MOVE_NONE;
|
||||
}
|
||||
|
||||
public int wouldSelectEdge(float x, float y) {
|
||||
int edgeSelected = calculateSelectedEdge(x, y);
|
||||
if (edgeSelected != MOVE_NONE && edgeSelected != MOVE_BLOCK) {
|
||||
return edgeSelected;
|
||||
}
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
public boolean selectEdge(int edge) {
|
||||
if (!checkValid(edge)) {
|
||||
// temporary
|
||||
throw new IllegalArgumentException("bad edge selected");
|
||||
// return false;
|
||||
}
|
||||
if ((mFixAspectRatio && !checkCorner(edge)) && !checkBlock(edge) && edge != MOVE_NONE) {
|
||||
// temporary
|
||||
throw new IllegalArgumentException("bad corner selected");
|
||||
// return false;
|
||||
}
|
||||
mMovingEdges = edge;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean selectEdge(float x, float y) {
|
||||
int edgeSelected = calculateSelectedEdge(x, y);
|
||||
if (mFixAspectRatio) {
|
||||
edgeSelected = fixEdgeToCorner(edgeSelected);
|
||||
}
|
||||
if (edgeSelected == MOVE_NONE) {
|
||||
return false;
|
||||
}
|
||||
return selectEdge(edgeSelected);
|
||||
}
|
||||
|
||||
public boolean moveCurrentSelection(float dX, float dY) {
|
||||
if (mMovingEdges == MOVE_NONE) {
|
||||
return false;
|
||||
}
|
||||
RectF crop = mBoundedRect.getInner();
|
||||
|
||||
float minWidthHeight = mMinSideSize;
|
||||
|
||||
int movingEdges = mMovingEdges;
|
||||
if (movingEdges == MOVE_BLOCK) {
|
||||
mBoundedRect.moveInner(dX, dY);
|
||||
return true;
|
||||
} else {
|
||||
float dx = 0;
|
||||
float dy = 0;
|
||||
|
||||
if ((movingEdges & MOVE_LEFT) != 0) {
|
||||
dx = Math.min(crop.left + dX, crop.right - minWidthHeight) - crop.left;
|
||||
}
|
||||
if ((movingEdges & MOVE_TOP) != 0) {
|
||||
dy = Math.min(crop.top + dY, crop.bottom - minWidthHeight) - crop.top;
|
||||
}
|
||||
if ((movingEdges & MOVE_RIGHT) != 0) {
|
||||
dx = Math.max(crop.right + dX, crop.left + minWidthHeight)
|
||||
- crop.right;
|
||||
}
|
||||
if ((movingEdges & MOVE_BOTTOM) != 0) {
|
||||
dy = Math.max(crop.bottom + dY, crop.top + minWidthHeight)
|
||||
- crop.bottom;
|
||||
}
|
||||
|
||||
if (mFixAspectRatio) {
|
||||
float[] l1 = {
|
||||
crop.left, crop.bottom
|
||||
};
|
||||
float[] l2 = {
|
||||
crop.right, crop.top
|
||||
};
|
||||
if (movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT) {
|
||||
l1[1] = crop.top;
|
||||
l2[1] = crop.bottom;
|
||||
}
|
||||
float[] b = {
|
||||
l1[0] - l2[0], l1[1] - l2[1]
|
||||
};
|
||||
float[] disp = {
|
||||
dx, dy
|
||||
};
|
||||
float[] bUnit = Utils.normalize(b);
|
||||
float sp = Utils.scalarProjection(disp, bUnit);
|
||||
dx = sp * bUnit[0];
|
||||
dy = sp * bUnit[1];
|
||||
RectF newCrop = fixedCornerResize(crop, movingEdges, dx, dy);
|
||||
|
||||
mBoundedRect.fixedAspectResizeInner(newCrop);
|
||||
} else {
|
||||
if ((movingEdges & MOVE_LEFT) != 0) {
|
||||
crop.left += dx;
|
||||
}
|
||||
if ((movingEdges & MOVE_TOP) != 0) {
|
||||
crop.top += dy;
|
||||
}
|
||||
if ((movingEdges & MOVE_RIGHT) != 0) {
|
||||
crop.right += dx;
|
||||
}
|
||||
if ((movingEdges & MOVE_BOTTOM) != 0) {
|
||||
crop.bottom += dy;
|
||||
}
|
||||
mBoundedRect.resizeInner(crop);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
private int calculateSelectedEdge(float x, float y) {
|
||||
RectF cropped = mBoundedRect.getInner();
|
||||
|
||||
float left = Math.abs(x - cropped.left);
|
||||
float right = Math.abs(x - cropped.right);
|
||||
float top = Math.abs(y - cropped.top);
|
||||
float bottom = Math.abs(y - cropped.bottom);
|
||||
|
||||
int edgeSelected = MOVE_NONE;
|
||||
// Check left or right.
|
||||
if ((left <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top)
|
||||
&& ((y - mTouchTolerance) <= cropped.bottom) && (left < right)) {
|
||||
edgeSelected |= MOVE_LEFT;
|
||||
}
|
||||
else if ((right <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top)
|
||||
&& ((y - mTouchTolerance) <= cropped.bottom)) {
|
||||
edgeSelected |= MOVE_RIGHT;
|
||||
}
|
||||
|
||||
// Check top or bottom.
|
||||
if ((top <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
|
||||
&& ((x - mTouchTolerance) <= cropped.right) && (top < bottom)) {
|
||||
edgeSelected |= MOVE_TOP;
|
||||
}
|
||||
else if ((bottom <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
|
||||
&& ((x - mTouchTolerance) <= cropped.right)) {
|
||||
edgeSelected |= MOVE_BOTTOM;
|
||||
}
|
||||
return edgeSelected;
|
||||
}
|
||||
|
||||
private static RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy) {
|
||||
RectF newCrop = null;
|
||||
// Fix opposite corner in place and move sides
|
||||
if (moving_corner == BOTTOM_RIGHT) {
|
||||
newCrop = new RectF(r.left, r.top, r.left + r.width() + dx, r.top + r.height()
|
||||
+ dy);
|
||||
} else if (moving_corner == BOTTOM_LEFT) {
|
||||
newCrop = new RectF(r.right - r.width() + dx, r.top, r.right, r.top + r.height()
|
||||
+ dy);
|
||||
} else if (moving_corner == TOP_LEFT) {
|
||||
newCrop = new RectF(r.right - r.width() + dx, r.bottom - r.height() + dy,
|
||||
r.right, r.bottom);
|
||||
} else if (moving_corner == TOP_RIGHT) {
|
||||
newCrop = new RectF(r.left, r.bottom - r.height() + dy, r.left
|
||||
+ r.width() + dx, r.bottom);
|
||||
}
|
||||
return newCrop;
|
||||
}
|
||||
|
||||
private static int fixEdgeToCorner(int moving_edges) {
|
||||
if (moving_edges == MOVE_LEFT) {
|
||||
moving_edges |= MOVE_TOP;
|
||||
}
|
||||
if (moving_edges == MOVE_TOP) {
|
||||
moving_edges |= MOVE_LEFT;
|
||||
}
|
||||
if (moving_edges == MOVE_RIGHT) {
|
||||
moving_edges |= MOVE_BOTTOM;
|
||||
}
|
||||
if (moving_edges == MOVE_BOTTOM) {
|
||||
moving_edges |= MOVE_RIGHT;
|
||||
}
|
||||
return moving_edges;
|
||||
}
|
||||
|
||||
}
|
||||
378
uikit/src/main/java/com/fengliyan/uikit/crop/app/CropView.java
Normal file
378
uikit/src/main/java/com/fengliyan/uikit/crop/app/CropView.java
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.fengliyan.uikit.crop.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.DashPathEffect;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.NinePatchDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
|
||||
public class CropView extends View {
|
||||
private static final String LOGTAG = "CropView";
|
||||
|
||||
private RectF mImageBounds = new RectF();
|
||||
private RectF mScreenBounds = new RectF();
|
||||
private RectF mScreenImageBounds = new RectF();
|
||||
private RectF mScreenCropBounds = new RectF();
|
||||
private Rect mShadowBounds = new Rect();
|
||||
|
||||
private Bitmap mBitmap;
|
||||
private Paint mPaint = new Paint();
|
||||
|
||||
private NinePatchDrawable mShadow;
|
||||
private CropObject mCropObj = null;
|
||||
private Drawable mCropIndicator;
|
||||
private int mIndicatorSize;
|
||||
private int mRotation = 0;
|
||||
private boolean mMovingBlock = false;
|
||||
private Matrix mDisplayMatrix = null;
|
||||
private Matrix mDisplayMatrixInverse = null;
|
||||
private boolean mDirty = false;
|
||||
|
||||
private float mPrevX = 0;
|
||||
private float mPrevY = 0;
|
||||
private float mSpotX = 0;
|
||||
private float mSpotY = 0;
|
||||
private boolean mDoSpot = false;
|
||||
|
||||
private int mShadowMargin = 15;
|
||||
private int mMargin = 32;
|
||||
private int mOverlayShadowColor = 0xCF000000;
|
||||
private int mOverlayWPShadowColor = 0x5F000000;
|
||||
private int mWPMarkerColor = 0x7FFFFFFF;
|
||||
private int mMinSideSize = 90;
|
||||
private int mTouchTolerance = 40;
|
||||
private float mDashOnLength = 20;
|
||||
private float mDashOffLength = 10;
|
||||
|
||||
private enum Mode {
|
||||
NONE, MOVE
|
||||
}
|
||||
|
||||
private Mode mState = Mode.NONE;
|
||||
|
||||
public CropView(Context context) {
|
||||
super(context);
|
||||
setup(context);
|
||||
}
|
||||
|
||||
public CropView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setup(context);
|
||||
}
|
||||
|
||||
public CropView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
setup(context);
|
||||
}
|
||||
|
||||
private void setup(Context context) {
|
||||
Resources rsc = context.getResources();
|
||||
mShadow = (NinePatchDrawable) rsc.getDrawable(R.drawable.muna_geometry_shadow);
|
||||
mCropIndicator = rsc.getDrawable(R.drawable.muna_camera_crop);
|
||||
mIndicatorSize = (int) rsc.getDimension(R.dimen.crop_indicator_size);
|
||||
mShadowMargin = (int) rsc.getDimension(R.dimen.shadow_margin);
|
||||
mMargin = (int) rsc.getDimension(R.dimen.preview_margin);
|
||||
mMinSideSize = (int) rsc.getDimension(R.dimen.crop_min_side);
|
||||
mTouchTolerance = (int) rsc.getDimension(R.dimen.crop_touch_tolerance);
|
||||
mOverlayShadowColor = (int) rsc.getColor(R.color.crop_shadow_color);
|
||||
mOverlayWPShadowColor = (int) rsc.getColor(R.color.crop_shadow_wp_color);
|
||||
mWPMarkerColor = (int) rsc.getColor(R.color.crop_wp_markers);
|
||||
mDashOnLength = rsc.getDimension(R.dimen.wp_selector_dash_length);
|
||||
mDashOffLength = rsc.getDimension(R.dimen.wp_selector_off_length);
|
||||
}
|
||||
|
||||
public void initialize(Bitmap image, RectF newCropBounds, RectF newPhotoBounds, int rotation) {
|
||||
mBitmap = image;
|
||||
if (mCropObj != null) {
|
||||
RectF crop = mCropObj.getInnerBounds();
|
||||
RectF containing = mCropObj.getOuterBounds();
|
||||
if (crop != newCropBounds || containing != newPhotoBounds
|
||||
|| mRotation != rotation) {
|
||||
mRotation = rotation;
|
||||
mCropObj.resetBoundsTo(newCropBounds, newPhotoBounds);
|
||||
clearDisplay();
|
||||
}
|
||||
} else {
|
||||
mRotation = rotation;
|
||||
mCropObj = new CropObject(newPhotoBounds, newCropBounds, 0);
|
||||
clearDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
public RectF getCrop() {
|
||||
return mCropObj.getInnerBounds();
|
||||
}
|
||||
|
||||
public RectF getPhoto() {
|
||||
return mCropObj.getOuterBounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
if (mDisplayMatrix == null || mDisplayMatrixInverse == null) {
|
||||
return true;
|
||||
}
|
||||
float[] touchPoint = {
|
||||
x, y
|
||||
};
|
||||
mDisplayMatrixInverse.mapPoints(touchPoint);
|
||||
x = touchPoint[0];
|
||||
y = touchPoint[1];
|
||||
switch (event.getActionMasked()) {
|
||||
case (MotionEvent.ACTION_DOWN):
|
||||
if (mState == Mode.NONE) {
|
||||
if (!mCropObj.selectEdge(x, y)) {
|
||||
mMovingBlock = mCropObj.selectEdge(CropObject.MOVE_BLOCK);
|
||||
}
|
||||
mPrevX = x;
|
||||
mPrevY = y;
|
||||
mState = Mode.MOVE;
|
||||
}
|
||||
break;
|
||||
case (MotionEvent.ACTION_UP):
|
||||
if (mState == Mode.MOVE) {
|
||||
mCropObj.selectEdge(CropObject.MOVE_NONE);
|
||||
mMovingBlock = false;
|
||||
mPrevX = x;
|
||||
mPrevY = y;
|
||||
mState = Mode.NONE;
|
||||
}
|
||||
break;
|
||||
case (MotionEvent.ACTION_MOVE):
|
||||
if (mState == Mode.MOVE) {
|
||||
float dx = x - mPrevX;
|
||||
float dy = y - mPrevY;
|
||||
mCropObj.moveCurrentSelection(dx, dy);
|
||||
mPrevX = x;
|
||||
mPrevY = y;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
Log.w(LOGTAG, "crop reset called");
|
||||
mState = Mode.NONE;
|
||||
mCropObj = null;
|
||||
mRotation = 0;
|
||||
mMovingBlock = false;
|
||||
clearDisplay();
|
||||
}
|
||||
|
||||
private void clearDisplay() {
|
||||
mDisplayMatrix = null;
|
||||
mDisplayMatrixInverse = null;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
protected void configChanged() {
|
||||
mDirty = true;
|
||||
}
|
||||
|
||||
public void applyFreeAspect() {
|
||||
mCropObj.unsetAspectRatio();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void applyOriginalAspect() {
|
||||
RectF outer = mCropObj.getOuterBounds();
|
||||
float w = outer.width();
|
||||
float h = outer.height();
|
||||
if (w > 0 && h > 0) {
|
||||
applyAspect(w, h);
|
||||
mCropObj.resetBoundsTo(outer, outer);
|
||||
} else {
|
||||
Log.w(LOGTAG, "failed to set aspect ratio original");
|
||||
}
|
||||
}
|
||||
|
||||
public void applySquareAspect() {
|
||||
applyAspect(1, 1);
|
||||
}
|
||||
|
||||
public void applyAspect(float x, float y) {
|
||||
if (x <= 0 || y <= 0) {
|
||||
throw new IllegalArgumentException("Bad arguments to applyAspect");
|
||||
}
|
||||
// If we are rotated by 90 degrees from horizontal, swap x and y
|
||||
if (((mRotation < 0) ? -mRotation : mRotation) % 180 == 90) {
|
||||
float tmp = x;
|
||||
x = y;
|
||||
y = tmp;
|
||||
}
|
||||
if (!mCropObj.setInnerAspectRatio(x, y)) {
|
||||
Log.w(LOGTAG, "failed to set aspect ratio");
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setWallpaperSpotlight(float spotlightX, float spotlightY) {
|
||||
mSpotX = spotlightX;
|
||||
mSpotY = spotlightY;
|
||||
if (mSpotX > 0 && mSpotY > 0) {
|
||||
mDoSpot = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void unsetWallpaperSpotlight() {
|
||||
mDoSpot = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates first d bits in integer x to the left some number of times.
|
||||
*/
|
||||
private int bitCycleLeft(int x, int times, int d) {
|
||||
int mask = (1 << d) - 1;
|
||||
int mout = x & mask;
|
||||
times %= d;
|
||||
int hi = mout >> (d - times);
|
||||
int low = (mout << times) & mask;
|
||||
int ret = x & ~mask;
|
||||
ret |= low;
|
||||
ret |= hi;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the selected edge or corner in screen coordinates.
|
||||
*/
|
||||
private int decode(int movingEdges, float rotation) {
|
||||
int rot = CropMath.constrainedRotation(rotation);
|
||||
switch (rot) {
|
||||
case 90:
|
||||
return bitCycleLeft(movingEdges, 1, 4);
|
||||
case 180:
|
||||
return bitCycleLeft(movingEdges, 2, 4);
|
||||
case 270:
|
||||
return bitCycleLeft(movingEdges, 3, 4);
|
||||
default:
|
||||
return movingEdges;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
if (mBitmap == null) {
|
||||
return;
|
||||
}
|
||||
if (mDirty) {
|
||||
mDirty = false;
|
||||
clearDisplay();
|
||||
}
|
||||
|
||||
mImageBounds = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
|
||||
mScreenBounds = new RectF(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
mScreenBounds.inset(mMargin, mMargin);
|
||||
|
||||
// If crop object doesn't exist, create it and update it from master
|
||||
// state
|
||||
if (mCropObj == null) {
|
||||
reset();
|
||||
mCropObj = new CropObject(mImageBounds, mImageBounds, 0);
|
||||
}
|
||||
|
||||
// If display matrix doesn't exist, create it and its dependencies
|
||||
if (mDisplayMatrix == null || mDisplayMatrixInverse == null) {
|
||||
mDisplayMatrix = new Matrix();
|
||||
mDisplayMatrix.reset();
|
||||
if (!CropDrawingUtils.setImageToScreenMatrix(mDisplayMatrix, mImageBounds, mScreenBounds,
|
||||
mRotation)) {
|
||||
Log.w(LOGTAG, "failed to get screen matrix");
|
||||
mDisplayMatrix = null;
|
||||
return;
|
||||
}
|
||||
mDisplayMatrixInverse = new Matrix();
|
||||
mDisplayMatrixInverse.reset();
|
||||
if (!mDisplayMatrix.invert(mDisplayMatrixInverse)) {
|
||||
Log.w(LOGTAG, "could not invert display matrix");
|
||||
mDisplayMatrixInverse = null;
|
||||
return;
|
||||
}
|
||||
// Scale min side and tolerance by display matrix scale factor
|
||||
mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize));
|
||||
mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance));
|
||||
}
|
||||
|
||||
mScreenImageBounds.set(mImageBounds);
|
||||
|
||||
// Draw background shadow
|
||||
if (mDisplayMatrix.mapRect(mScreenImageBounds)) {
|
||||
int margin = (int) mDisplayMatrix.mapRadius(mShadowMargin);
|
||||
mScreenImageBounds.roundOut(mShadowBounds);
|
||||
mShadowBounds.set(mShadowBounds.left - margin, mShadowBounds.top -
|
||||
margin, mShadowBounds.right + margin, mShadowBounds.bottom + margin);
|
||||
mShadow.setBounds(mShadowBounds);
|
||||
mShadow.draw(canvas);
|
||||
}
|
||||
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setFilterBitmap(true);
|
||||
// Draw actual bitmap
|
||||
canvas.drawBitmap(mBitmap, mDisplayMatrix, mPaint);
|
||||
|
||||
mCropObj.getInnerBounds(mScreenCropBounds);
|
||||
|
||||
if (mDisplayMatrix.mapRect(mScreenCropBounds)) {
|
||||
|
||||
// Draw overlay shadows
|
||||
Paint p = new Paint();
|
||||
p.setColor(mOverlayShadowColor);
|
||||
p.setStyle(Paint.Style.FILL);
|
||||
CropDrawingUtils.drawShadows(canvas, p, mScreenCropBounds, mScreenImageBounds);
|
||||
|
||||
// Draw crop rect and markers
|
||||
CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds);
|
||||
if (!mDoSpot) {
|
||||
CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds);
|
||||
} else {
|
||||
Paint wpPaint = new Paint();
|
||||
wpPaint.setColor(mWPMarkerColor);
|
||||
wpPaint.setStrokeWidth(3);
|
||||
wpPaint.setStyle(Paint.Style.STROKE);
|
||||
wpPaint.setPathEffect(new DashPathEffect(new float[]
|
||||
{mDashOnLength, mDashOnLength + mDashOffLength}, 0));
|
||||
p.setColor(mOverlayWPShadowColor);
|
||||
CropDrawingUtils.drawWallpaperSelectionFrame(canvas, mScreenCropBounds,
|
||||
mSpotX, mSpotY, wpPaint, p);
|
||||
}
|
||||
CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize,
|
||||
mScreenCropBounds, mCropObj.isFixedAspect(), decode(mCropObj.getSelectState(), mRotation));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
432
uikit/src/main/java/com/fengliyan/uikit/crop/app/Utils.java
Normal file
432
uikit/src/main/java/com/fengliyan/uikit/crop/app/Utils.java
Normal file
@@ -0,0 +1,432 @@
|
||||
package com.fengliyan.uikit.crop.app;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.MediaStore.Images;
|
||||
import android.provider.MediaStore.Images.ImageColumns;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Created by neu on 16/1/12.
|
||||
*/
|
||||
public class Utils {
|
||||
private static final String LOGTAG = "SaveImage";
|
||||
|
||||
public static final int ORI_ROTATE_90 = 6;
|
||||
public static final int ORI_ROTATE_180 = 3;
|
||||
public static final int ORI_ROTATE_270 = 8;
|
||||
public static final short ORI_NORMAL = 1;
|
||||
private static final String TIME_STAMP_NAME = "_yyyyMMdd_HHmmss";
|
||||
public static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos";
|
||||
public static final String JPEG_MIME_TYPE = "image/jpeg";
|
||||
|
||||
|
||||
// Math operations for 2d vectors
|
||||
public static float clamp(float i, float low, float high) {
|
||||
return Math.max(Math.min(i, high), low);
|
||||
}
|
||||
|
||||
public static float vectorLength(float[] a) {
|
||||
return (float) Math.hypot(a[0], a[1]);
|
||||
}
|
||||
public static float[] shortestVectorFromPointToLine(float[] point, float[] line) {
|
||||
float x1 = line[0];
|
||||
float x2 = line[2];
|
||||
float y1 = line[1];
|
||||
float y2 = line[3];
|
||||
float xdelt = x2 - x1;
|
||||
float ydelt = y2 - y1;
|
||||
if (xdelt == 0 && ydelt == 0)
|
||||
return null;
|
||||
float u = ((point[0] - x1) * xdelt + (point[1] - y1) * ydelt)
|
||||
/ (xdelt * xdelt + ydelt * ydelt);
|
||||
float[] ret = {
|
||||
(x1 + u * (x2 - x1)), (y1 + u * (y2 - y1))
|
||||
};
|
||||
float[] vec = {
|
||||
ret[0] - point[0], ret[1] - point[1]
|
||||
};
|
||||
return vec;
|
||||
}
|
||||
|
||||
public static float[] lineIntersect(float[] line1, float[] line2) {
|
||||
float a0 = line1[0];
|
||||
float a1 = line1[1];
|
||||
float b0 = line1[2];
|
||||
float b1 = line1[3];
|
||||
float c0 = line2[0];
|
||||
float c1 = line2[1];
|
||||
float d0 = line2[2];
|
||||
float d1 = line2[3];
|
||||
float t0 = a0 - b0;
|
||||
float t1 = a1 - b1;
|
||||
float t2 = b0 - d0;
|
||||
float t3 = d1 - b1;
|
||||
float t4 = c0 - d0;
|
||||
float t5 = c1 - d1;
|
||||
|
||||
float denom = t1 * t4 - t0 * t5;
|
||||
if (denom == 0)
|
||||
return null;
|
||||
float u = (t3 * t4 + t5 * t2) / denom;
|
||||
float[] intersect = {
|
||||
b0 + u * t0, b1 + u * t1
|
||||
};
|
||||
return intersect;
|
||||
}
|
||||
|
||||
public static float[] normalize(float[] a) {
|
||||
float length = (float) Math.hypot(a[0], a[1]);
|
||||
float[] b = {
|
||||
a[0] / length, a[1] / length
|
||||
};
|
||||
return b;
|
||||
}
|
||||
|
||||
// A onto B
|
||||
public static float scalarProjection(float[] a, float[] b) {
|
||||
float length = (float) Math.hypot(b[0], b[1]);
|
||||
return dotProduct(a, b) / length;
|
||||
}
|
||||
|
||||
// A . B
|
||||
public static float dotProduct(float[] a, float[] b) {
|
||||
return a[0] * b[0] + a[1] * b[1];
|
||||
}
|
||||
|
||||
public static void closeSilently(Closeable c) {
|
||||
if (c == null) return;
|
||||
try {
|
||||
c.close();
|
||||
} catch (Throwable t) {
|
||||
//Log.w(TAG, "close fail ", t);
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap loadConstrainedBitmap(Uri uri, Context context, int maxSideLength,
|
||||
Rect originalBounds, boolean useMin) {
|
||||
if (maxSideLength <= 0 || uri == null || context == null) {
|
||||
throw new IllegalArgumentException("bad argument to getScaledBitmap");
|
||||
}
|
||||
// Get width and height of stored bitmap
|
||||
Rect storedBounds = loadBitmapBounds(context, uri);
|
||||
if (originalBounds != null) {
|
||||
originalBounds.set(storedBounds);
|
||||
}
|
||||
int w = storedBounds.width();
|
||||
int h = storedBounds.height();
|
||||
|
||||
// If bitmap cannot be decoded, return null
|
||||
if (w <= 0 || h <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find best downsampling size
|
||||
int imageSide = 0;
|
||||
if (useMin) {
|
||||
imageSide = Math.min(w, h);
|
||||
} else {
|
||||
imageSide = Math.max(w, h);
|
||||
}
|
||||
int sampleSize = 1;
|
||||
while (imageSide > maxSideLength) {
|
||||
imageSide >>>= 1;
|
||||
sampleSize <<= 1;
|
||||
}
|
||||
|
||||
// Make sure sample size is reasonable
|
||||
if (sampleSize <= 0 ||
|
||||
0 >= (int) (Math.min(w, h) / sampleSize)) {
|
||||
return null;
|
||||
}
|
||||
return loadDownsampledBitmap(context, uri, sampleSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds of the bitmap stored at a given Url.
|
||||
*/
|
||||
public static Rect loadBitmapBounds(Context context, Uri uri) {
|
||||
BitmapFactory.Options o = new BitmapFactory.Options();
|
||||
o.inJustDecodeBounds = true;
|
||||
loadBitmap(context, uri, o);
|
||||
return new Rect(0, 0, o.outWidth, o.outHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a bitmap that has been downsampled using sampleSize from a given url.
|
||||
*/
|
||||
public static Bitmap loadDownsampledBitmap(Context context, Uri uri, int sampleSize) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inMutable = true;
|
||||
options.inSampleSize = sampleSize;
|
||||
return loadBitmap(context, uri, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bitmap from the given uri loaded using the given options.
|
||||
* Returns null on failure.
|
||||
*/
|
||||
public static Bitmap loadBitmap(Context context, Uri uri, BitmapFactory.Options o) {
|
||||
if (uri == null || context == null) {
|
||||
throw new IllegalArgumentException("bad argument to loadBitmap");
|
||||
}
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = context.getContentResolver().openInputStream(uri);
|
||||
return BitmapFactory.decodeStream(is, null, o);
|
||||
} catch (FileNotFoundException e) {
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
|
||||
} finally {
|
||||
Utils.closeSilently(is);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rotation of image at the given URI as one of 0, 90, 180,
|
||||
* 270. Defaults to 0.
|
||||
*/
|
||||
public static int getMetadataRotation(Context context, Uri uri) {
|
||||
int orientation = getMetadataOrientation(context, uri);
|
||||
switch (orientation) {
|
||||
case ORI_ROTATE_90:
|
||||
return 90;
|
||||
case ORI_ROTATE_180:
|
||||
return 180;
|
||||
case ORI_ROTATE_270:
|
||||
return 270;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image's orientation flag. Defaults to ORI_NORMAL if no valid
|
||||
* orientation was found.
|
||||
*/
|
||||
public static int getMetadataOrientation(Context context, Uri uri) {
|
||||
if (uri == null || context == null) {
|
||||
throw new IllegalArgumentException("bad argument to getOrientation");
|
||||
}
|
||||
|
||||
// First try to find orientation data in Gallery's ContentProvider.
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri,
|
||||
new String[] { ImageColumns.ORIENTATION },
|
||||
null, null, null);
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
int ori = cursor.getInt(0);
|
||||
switch (ori) {
|
||||
case 90:
|
||||
return ORI_ROTATE_90;
|
||||
case 270:
|
||||
return ORI_ROTATE_270;
|
||||
case 180:
|
||||
return ORI_ROTATE_180;
|
||||
default:
|
||||
return ORI_NORMAL;
|
||||
}
|
||||
}
|
||||
} catch (SQLiteException e) {
|
||||
// Do nothing
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Do nothing
|
||||
} catch (IllegalStateException e) {
|
||||
// Do nothing
|
||||
} finally {
|
||||
Utils.closeSilently(cursor);
|
||||
}
|
||||
|
||||
return ORI_NORMAL;
|
||||
}
|
||||
|
||||
public static Uri makeAndInsertUri(Context context, Uri sourceUri) {
|
||||
long time = System.currentTimeMillis();
|
||||
String filename = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date(time));
|
||||
File saveDirectory = getFinalSaveDirectory(context, sourceUri);
|
||||
File file = new File(saveDirectory, filename + ".JPG");
|
||||
return linkNewFileToUri(context, sourceUri, file, time, false);
|
||||
}
|
||||
|
||||
public static File getFinalSaveDirectory(Context context, Uri sourceUri) {
|
||||
File saveDirectory = getSaveDirectory(context, sourceUri);
|
||||
if ((saveDirectory == null) || !saveDirectory.canWrite()) {
|
||||
saveDirectory = new File(context.getExternalFilesDir(""),
|
||||
DEFAULT_SAVE_DIRECTORY);
|
||||
}
|
||||
// Create the directory if it doesn't exist
|
||||
if (!saveDirectory.exists())
|
||||
saveDirectory.mkdirs();
|
||||
return saveDirectory;
|
||||
}
|
||||
|
||||
private static File getSaveDirectory(Context context, Uri sourceUri) {
|
||||
File file = getLocalFileFromUri(context, sourceUri);
|
||||
if (file != null) {
|
||||
return file.getParentFile();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Uri linkNewFileToUri(Context context, Uri sourceUri,
|
||||
File file, long time, boolean deleteOriginal) {
|
||||
File oldSelectedFile = getLocalFileFromUri(context, sourceUri);
|
||||
final ContentValues values = getContentValues(context, sourceUri, file, time);
|
||||
|
||||
Uri result = sourceUri;
|
||||
|
||||
// In the case of incoming Uri is just a local file Uri (like a cached
|
||||
// file), we can't just update the Uri. We have to create a new Uri.
|
||||
boolean fileUri = isFileUri(sourceUri);
|
||||
|
||||
if (fileUri || oldSelectedFile == null || !deleteOriginal) {
|
||||
result = context.getContentResolver().insert(
|
||||
Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} else {
|
||||
context.getContentResolver().update(sourceUri, values, null, null);
|
||||
if (oldSelectedFile.exists()) {
|
||||
oldSelectedFile.delete();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* @param sourceUri
|
||||
* @return true if the sourceUri is a local file Uri.
|
||||
*/
|
||||
private static boolean isFileUri(Uri sourceUri) {
|
||||
String scheme = sourceUri.getScheme();
|
||||
if (scheme != null && scheme.equals(ContentResolver.SCHEME_FILE)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static ContentValues getContentValues(Context context, Uri sourceUri,
|
||||
File file, long time) {
|
||||
final ContentValues values = new ContentValues();
|
||||
|
||||
time /= 1000;
|
||||
values.put(Images.Media.TITLE, file.getName());
|
||||
values.put(Images.Media.DISPLAY_NAME, file.getName());
|
||||
values.put(Images.Media.MIME_TYPE, "image/jpeg");
|
||||
values.put(Images.Media.DATE_TAKEN, time);
|
||||
values.put(Images.Media.DATE_MODIFIED, time);
|
||||
values.put(Images.Media.DATE_ADDED, time);
|
||||
values.put(Images.Media.ORIENTATION, 0);
|
||||
values.put(Images.Media.DATA, file.getAbsolutePath());
|
||||
values.put(Images.Media.SIZE, file.length());
|
||||
// This is a workaround to trigger the MediaProvider to re-generate the
|
||||
// thumbnail.
|
||||
values.put(Images.Media.MINI_THUMB_MAGIC, 0);
|
||||
|
||||
final String[] projection = new String[] {
|
||||
ImageColumns.DATE_TAKEN,
|
||||
ImageColumns.LATITUDE, ImageColumns.LONGITUDE,
|
||||
};
|
||||
|
||||
querySource(context, sourceUri, projection,
|
||||
new ContentResolverQueryCallback() {
|
||||
|
||||
@Override
|
||||
public void onCursorResult(Cursor cursor) {
|
||||
values.put(Images.Media.DATE_TAKEN, cursor.getLong(0));
|
||||
|
||||
double latitude = cursor.getDouble(1);
|
||||
double longitude = cursor.getDouble(2);
|
||||
// issue is fixed.
|
||||
if ((latitude != 0f) || (longitude != 0f)) {
|
||||
values.put(Images.Media.LATITUDE, latitude);
|
||||
values.put(Images.Media.LONGITUDE, longitude);
|
||||
}
|
||||
}
|
||||
});
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a File object based on the srcUri.
|
||||
* @return The file object. Return null if srcUri is invalid or not a local
|
||||
* file.
|
||||
*/
|
||||
private static File getLocalFileFromUri(Context context, Uri srcUri) {
|
||||
if (srcUri == null) {
|
||||
Log.e(LOGTAG, "srcUri is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
String scheme = srcUri.getScheme();
|
||||
if (scheme == null) {
|
||||
Log.e(LOGTAG, "scheme is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
final File[] file = new File[1];
|
||||
// sourceUri can be a file path or a content Uri, it need to be handled
|
||||
// differently.
|
||||
if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
|
||||
if (srcUri.getAuthority().equals(MediaStore.AUTHORITY)) {
|
||||
querySource(context, srcUri, new String[] {
|
||||
ImageColumns.DATA
|
||||
},
|
||||
new ContentResolverQueryCallback() {
|
||||
|
||||
@Override
|
||||
public void onCursorResult(Cursor cursor) {
|
||||
file[0] = new File(cursor.getString(0));
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (scheme.equals(ContentResolver.SCHEME_FILE)) {
|
||||
file[0] = new File(srcUri.getPath());
|
||||
}
|
||||
return file[0];
|
||||
}
|
||||
|
||||
public static void querySource(Context context, Uri sourceUri, String[] projection,
|
||||
ContentResolverQueryCallback callback) {
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
querySourceFromContentResolver(contentResolver, sourceUri, projection, callback);
|
||||
}
|
||||
private static void querySourceFromContentResolver(
|
||||
ContentResolver contentResolver, Uri sourceUri, String[] projection,
|
||||
ContentResolverQueryCallback callback) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = contentResolver.query(sourceUri, projection, null, null,
|
||||
null);
|
||||
if ((cursor != null) && cursor.moveToNext()) {
|
||||
callback.onCursorResult(cursor);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore error for lacking the data column from the source.
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ContentResolverQueryCallback {
|
||||
void onCursorResult(Cursor cursor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.Window;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
|
||||
public class BaseDialog extends Dialog {
|
||||
|
||||
private static final String TAG = "BaseDialog";
|
||||
|
||||
private Context mContext;
|
||||
|
||||
public BaseDialog(Context context) {
|
||||
super(context, android.R.style.Theme_Dialog);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
mContext = context;
|
||||
getWindow().setBackgroundDrawableResource(R.drawable.muna_base_dialog_bg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(int layoutResID) {
|
||||
View view = View.inflate(mContext, layoutResID, null);
|
||||
LayoutParams params = new LayoutParams((int) mContext.getResources().getDimension(R.dimen.muna_base_dialog_width), LayoutParams.MATCH_PARENT);
|
||||
super.setContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
LayoutParams params = new LayoutParams((int) mContext.getResources().getDimension(R.dimen.muna_base_dialog_width), LayoutParams.MATCH_PARENT);
|
||||
super.setContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
try {
|
||||
super.show();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
try {
|
||||
super.dismiss();
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
|
||||
/**
|
||||
* 统一的消息提示对话框
|
||||
*/
|
||||
public class BaseMessageDialog extends BaseDialog {
|
||||
|
||||
/**
|
||||
* 一个按钮的消息对话框
|
||||
*/
|
||||
public static final int TYPE_ONE_BUTTON = 1;
|
||||
/**
|
||||
* 两个按钮的消息对话框
|
||||
*/
|
||||
public static final int TYPE_TWO_BUTTON = 2;
|
||||
|
||||
private int mType = TYPE_ONE_BUTTON;
|
||||
private DialogInterface.OnClickListener mPositiveButtonListener = null;
|
||||
private DialogInterface.OnClickListener mNegativeButtonListener = null;
|
||||
private Button confirm;
|
||||
private Button cancel;
|
||||
private Button okBtn;
|
||||
|
||||
private CharSequence mMessage;
|
||||
private CharSequence mPositive;
|
||||
private CharSequence mNegative;
|
||||
private CharSequence mTitle;
|
||||
|
||||
private View oneBtnLayout;
|
||||
private View twoBtnLayout;
|
||||
|
||||
private View mCustomView;
|
||||
|
||||
private int mHighLightButton;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*/
|
||||
public BaseMessageDialog(Context context) {
|
||||
this(context, TYPE_ONE_BUTTON);
|
||||
}
|
||||
|
||||
public BaseMessageDialog(Context context, int type) {
|
||||
super(context);
|
||||
this.mType = type;
|
||||
init();
|
||||
}
|
||||
|
||||
private BaseMessageDialog(Builder builder) {
|
||||
super(builder.mContext);
|
||||
mPositiveButtonListener = builder.mPositiveButtonListener;
|
||||
mNegativeButtonListener = builder.mNegativeButtonListener;
|
||||
mMessage = builder.mMessage;
|
||||
mPositive = builder.mPositive;
|
||||
mNegative = builder.mNegative;
|
||||
mTitle = builder.mTitle;
|
||||
mCustomView = builder.mCustomView;
|
||||
if (mNegative == null && mNegativeButtonListener == null) {
|
||||
mType = TYPE_ONE_BUTTON;
|
||||
} else {
|
||||
mType = TYPE_TWO_BUTTON;
|
||||
}
|
||||
mHighLightButton = builder.mHighLightButton;
|
||||
setCancelable(builder.mCancelable);
|
||||
setOnCancelListener(builder.mOnCancelListener);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setContentView(R.layout.muna_base_dialog_message);
|
||||
confirm = (Button) findViewById(R.id.base_message_positive_button);
|
||||
cancel = (Button) findViewById(R.id.base_message_negative_button);
|
||||
okBtn = (Button) findViewById(R.id.base_message_ok_button);
|
||||
oneBtnLayout = findViewById(R.id.base_message_one_button_layout);
|
||||
twoBtnLayout = findViewById(R.id.base_message_two_button_layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
FrameLayout content = (FrameLayout) findViewById(R.id.base_message_content_text);
|
||||
if (mCustomView != null) {
|
||||
content.addView(mCustomView);
|
||||
} else if (TextUtils.isEmpty(mTitle)) {
|
||||
content.addView(View.inflate(getContext(), R.layout.muna_base_dialog_message_content_no_title, null));
|
||||
TextView messageText = (TextView) findViewById(R.id.base_message_message_text);
|
||||
messageText.setText(mMessage);
|
||||
} else {
|
||||
content.addView(View.inflate(getContext(), R.layout.muna_base_dialog_message_content, null));
|
||||
TextView titleText = (TextView) findViewById(R.id.base_message_title_text);
|
||||
titleText.setText(mTitle);
|
||||
TextView messageText = (TextView) findViewById(R.id.base_message_message_text);
|
||||
messageText.setText(mMessage);
|
||||
}
|
||||
|
||||
okBtn.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mPositiveButtonListener != null) {
|
||||
mPositiveButtonListener.onClick(BaseMessageDialog.this, BUTTON_POSITIVE);
|
||||
} else {
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
confirm.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mPositiveButtonListener != null) {
|
||||
mPositiveButtonListener.onClick(BaseMessageDialog.this, BUTTON_POSITIVE);
|
||||
} else {
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cancel.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mNegativeButtonListener != null) {
|
||||
mNegativeButtonListener.onClick(BaseMessageDialog.this, BUTTON_NEGATIVE);
|
||||
} else {
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (mType == TYPE_ONE_BUTTON) {
|
||||
oneBtnLayout.setVisibility(View.VISIBLE);
|
||||
twoBtnLayout.setVisibility(View.GONE);
|
||||
if (!TextUtils.isEmpty(mPositive)) {
|
||||
okBtn.setText(mPositive);
|
||||
}
|
||||
|
||||
if (mHighLightButton == BUTTON_POSITIVE) {
|
||||
okBtn.getPaint().setFakeBoldText(true);
|
||||
}
|
||||
} else {
|
||||
oneBtnLayout.setVisibility(View.GONE);
|
||||
twoBtnLayout.setVisibility(View.VISIBLE);
|
||||
if (!TextUtils.isEmpty(mPositive)) {
|
||||
confirm.setText(mPositive);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(mNegative)) {
|
||||
cancel.setText(mNegative);
|
||||
}
|
||||
|
||||
if (mHighLightButton == BUTTON_POSITIVE) {
|
||||
confirm.getPaint().setFakeBoldText(true);
|
||||
} else if (mHighLightButton == BUTTON_NEGATIVE) {
|
||||
cancel.getPaint().setFakeBoldText(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置标题
|
||||
*
|
||||
* @param title 标题内容
|
||||
*/
|
||||
public BaseMessageDialog setDialogTitle(CharSequence title) {
|
||||
this.mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseMessageDialog setDialogTitle(int resid) {
|
||||
this.mTitle = getContext().getResources().getString(resid);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置消息内容
|
||||
*
|
||||
* @param message 消息内容
|
||||
*/
|
||||
public BaseMessageDialog setMessage(CharSequence message) {
|
||||
this.mMessage = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseMessageDialog setMessage(int resid) {
|
||||
this.mMessage = getContext().getResources().getString(resid);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置确定按钮点击事件监听函数,当只有一个按钮时,此事件生效
|
||||
*
|
||||
* @param l 监听函数
|
||||
*/
|
||||
public BaseMessageDialog setPositiveButton(DialogInterface.OnClickListener l) {
|
||||
mPositiveButtonListener = l;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置取消按钮的监听事件,当只有一个按钮时,此事件无效
|
||||
*
|
||||
* @param l 监听函数
|
||||
*/
|
||||
public BaseMessageDialog setNegativeButton(DialogInterface.OnClickListener l) {
|
||||
mNegativeButtonListener = l;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置确定按钮的文字,当只有一个按钮时,此文字为按钮显示的文字
|
||||
*
|
||||
* @param text 按钮文字
|
||||
*/
|
||||
public BaseMessageDialog setPositiveText(CharSequence text) {
|
||||
mPositive = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置取消按钮的文字,当只有一个按钮时,此方法设置的文字不生效
|
||||
*
|
||||
* @param text 按钮文字
|
||||
*/
|
||||
public BaseMessageDialog setNegativeText(CharSequence text) {
|
||||
mNegative = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setPositiveEnable(boolean enable) {
|
||||
confirm.setEnabled(enable);
|
||||
okBtn.setEnabled(enable);
|
||||
}
|
||||
|
||||
public void setNegativeEnable(boolean enable) {
|
||||
cancel.setEnabled(enable);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Context mContext;
|
||||
private CharSequence mMessage;
|
||||
private CharSequence mPositive;
|
||||
private CharSequence mNegative;
|
||||
private CharSequence mTitle;
|
||||
private DialogInterface.OnClickListener mPositiveButtonListener = null;
|
||||
private DialogInterface.OnClickListener mNegativeButtonListener = null;
|
||||
private DialogInterface.OnCancelListener mOnCancelListener = null;
|
||||
private View mCustomView;
|
||||
private boolean mCancelable = true;
|
||||
private int mHighLightButton;
|
||||
|
||||
public Builder() {
|
||||
|
||||
}
|
||||
|
||||
public Builder(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置显示内容
|
||||
*/
|
||||
public Builder setMessage(CharSequence message) {
|
||||
this.mMessage = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置右边按钮标题
|
||||
*/
|
||||
public Builder setPositive(CharSequence positive) {
|
||||
this.mPositive = positive;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置左边按钮标题
|
||||
*/
|
||||
public Builder setNegative(CharSequence negative) {
|
||||
this.mNegative = negative;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置整个对话框标题
|
||||
*/
|
||||
public Builder setTitle(CharSequence title) {
|
||||
this.mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置右边按钮监听
|
||||
*/
|
||||
public Builder setPositiveButtonListener(DialogInterface.OnClickListener positiveButtonListener) {
|
||||
this.mPositiveButtonListener = positiveButtonListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置左边操作的按钮监听
|
||||
*/
|
||||
public Builder setNegativeButtonListener(DialogInterface.OnClickListener negativeButtonListener) {
|
||||
this.mNegativeButtonListener = negativeButtonListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置取消的监听
|
||||
*
|
||||
* @param onCancelListener 取消的监听
|
||||
*/
|
||||
public Builder setOnCancelListener(DialogInterface.OnCancelListener onCancelListener) {
|
||||
mOnCancelListener = onCancelListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置自定义的view,不包括底部的按钮
|
||||
*
|
||||
* @param customView 自定义view
|
||||
*/
|
||||
public Builder setCustomView(View customView) {
|
||||
mCustomView = customView;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setContext(Context context) {
|
||||
mContext = context;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置dialog是否可取消
|
||||
*
|
||||
* @param cancelable true 可以
|
||||
*/
|
||||
public Builder setCancelable(boolean cancelable) {
|
||||
mCancelable = cancelable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置粗体的按钮,取值有{@link BaseMessageDialog#BUTTON_POSITIVE},{@link BaseMessageDialog#BUTTON_NEGATIVE}
|
||||
*
|
||||
* @param highLightButton 设置粗体的按钮
|
||||
*/
|
||||
public Builder setHighLightButton(int highLightButton) {
|
||||
mHighLightButton = highLightButton;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseMessageDialog build() {
|
||||
return new BaseMessageDialog(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
public class BottomGiftDialog extends Dialog {
|
||||
private Context mContext;
|
||||
private ViewPager mViewPager;
|
||||
private LinearLayout mViewGroup;
|
||||
private TextView mCaiBiText;
|
||||
private View mCaiBiLayout;
|
||||
private Button mSendButton;
|
||||
private GiftListBean mGiftListBean;
|
||||
private GiftPagesManager mGiftManager;
|
||||
private OnGiftItemClickedListener mGiftListener;
|
||||
private TextView mTvGiveGift;
|
||||
|
||||
public interface OnGiftItemClickedListener {
|
||||
void onGiftClicked(DialogGiftBean bean);
|
||||
|
||||
void onWalletClicked();
|
||||
}
|
||||
|
||||
public void setOnGiftItemClickedListener(OnGiftItemClickedListener listener) {
|
||||
mGiftListener = listener;
|
||||
}
|
||||
|
||||
public BottomGiftDialog(@NonNull Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public BottomGiftDialog(@NonNull Context context, int themeResId) {
|
||||
super(context, R.style.SelectiveDialog);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
protected BottomGiftDialog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
|
||||
super(context, R.style.SelectiveDialog);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void setGiftListBean(GiftListBean bean) {
|
||||
mGiftListBean = bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.dialog_gift);
|
||||
initView();
|
||||
|
||||
Window window = this.getWindow();
|
||||
window.setGravity(Gravity.BOTTOM);
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
params.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
window.setAttributes(params);
|
||||
}
|
||||
|
||||
private DialogGiftBean mDialogGiftBean;
|
||||
|
||||
private void initView() {
|
||||
mViewPager = findViewById(R.id.gift_pager);
|
||||
mViewGroup = findViewById(R.id.gift_view_group);
|
||||
mCaiBiText = findViewById(R.id.gift_shell_number);
|
||||
mCaiBiLayout = findViewById(R.id.gift_shell_layout);
|
||||
mSendButton = findViewById(R.id.gift_button);
|
||||
mTvGiveGift = findViewById(R.id.tv_give_gift);
|
||||
mTvGiveGift.setEnabled(false);
|
||||
mCaiBiText.setText(mGiftListBean.getCoin() + "");
|
||||
mGiftManager = new GiftPagesManager(mViewPager, mViewGroup, mContext);
|
||||
mGiftManager.addGiftBean(mGiftListBean.getGift_list());
|
||||
mGiftManager.manage();
|
||||
mGiftManager.setOnGiftItemClickedListener(new GiftPagesManager.OnGiftItemClickedListener() {
|
||||
@Override
|
||||
public void onGiftClicked(DialogGiftBean bean) {
|
||||
mTvGiveGift.setEnabled(true);
|
||||
mDialogGiftBean = bean;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
mCaiBiLayout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (null != mGiftListener) {
|
||||
mGiftListener.onWalletClicked();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mTvGiveGift.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (null != mGiftListener) {
|
||||
mGiftListener.onGiftClicked(mDialogGiftBean);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setCoin(String coin) {
|
||||
mCaiBiText.setText(coin);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class BottomSelectiveDialog extends Dialog {
|
||||
|
||||
private Context mContext;
|
||||
private LinearLayout mSelectiveLayout;
|
||||
private TextView mCancelButton;
|
||||
private List<TextView> mButtonList = new ArrayList<>();
|
||||
private List<OnButtonSelectListener> mListenerList = new ArrayList<>();
|
||||
|
||||
public interface OnButtonSelectListener {
|
||||
void onClicked(View view, int index);
|
||||
}
|
||||
|
||||
public TextView addSelectButton(String buttonName, OnButtonSelectListener listener) {
|
||||
TextView textView = new TextView(mContext);
|
||||
LinearLayout.LayoutParams params = new LinearLayout
|
||||
.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 128);
|
||||
textView.setLayoutParams(params);
|
||||
textView.setText(buttonName);
|
||||
textView.setTextSize(17);
|
||||
textView.setTextColor(mContext.getResources().getColor(R.color.editTextColor));
|
||||
textView.setGravity(Gravity.CENTER);
|
||||
textView.setBackgroundColor(Color.WHITE);
|
||||
final int count = mButtonList.size();
|
||||
final OnButtonSelectListener tmplistener = listener;
|
||||
textView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (null != tmplistener) {
|
||||
tmplistener.onClicked(view, count);
|
||||
}
|
||||
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
mButtonList.add(textView);
|
||||
return textView;
|
||||
}
|
||||
|
||||
public void cleanButtons() {
|
||||
mButtonList.clear();
|
||||
}
|
||||
|
||||
public BottomSelectiveDialog(@NonNull Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public BottomSelectiveDialog(@NonNull Context context, int themeResId) {
|
||||
super(context, R.style.SelectiveDialog);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
protected BottomSelectiveDialog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
|
||||
super(context, R.style.SelectiveDialog);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.dialog_selector);
|
||||
initView();
|
||||
Iterator<TextView> iterator = mButtonList.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
TextView textView = iterator.next();
|
||||
mSelectiveLayout.addView(textView);
|
||||
View line = new View(mContext);
|
||||
LinearLayout.LayoutParams params = new LinearLayout
|
||||
.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 2);
|
||||
line.setBackgroundResource(R.color.bottomCuttingLine);
|
||||
line.setLayoutParams(params);
|
||||
mSelectiveLayout.addView(line);
|
||||
}
|
||||
|
||||
Window window = this.getWindow();
|
||||
window.setGravity(Gravity.BOTTOM);
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
params.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
window.setAttributes(params);
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
mSelectiveLayout = findViewById(R.id.selector_layout);
|
||||
mCancelButton = findViewById(R.id.selector_cancel);
|
||||
mCancelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
public class BottomShareDialog extends Dialog implements View.OnClickListener {
|
||||
private Context mContext;
|
||||
private View mErView;
|
||||
private View mWeixinView;
|
||||
private View mPengView;
|
||||
private View mQQView;
|
||||
private View mKongView;
|
||||
private View mWeiboView;
|
||||
private View mLinkView;
|
||||
private OnButtonSelectListener mListener;
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if(view == mErView){
|
||||
if(null != mListener){
|
||||
mListener.onClicked(view, 0);
|
||||
}
|
||||
}else if(view == mWeixinView){
|
||||
if(null != mListener){
|
||||
mListener.onClicked(view, 1);
|
||||
}
|
||||
}else if(view == mPengView){
|
||||
if(null != mListener){
|
||||
mListener.onClicked(view, 2);
|
||||
}
|
||||
}else if(view == mQQView){
|
||||
if(null != mListener){
|
||||
mListener.onClicked(view, 3);
|
||||
}
|
||||
}else if(view == mKongView){
|
||||
if(null != mListener){
|
||||
mListener.onClicked(view, 4);
|
||||
}
|
||||
}else if(view == mWeiboView){
|
||||
if(null != mListener){
|
||||
mListener.onClicked(view, 5);
|
||||
}
|
||||
}else if(view == mLinkView){
|
||||
if(null != mListener){
|
||||
mListener.onClicked(view, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnButtonSelectListener{
|
||||
void onClicked(View view, int index);
|
||||
}
|
||||
|
||||
public void setOnButtonSelectListener(OnButtonSelectListener listener){
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public BottomShareDialog(@NonNull Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public BottomShareDialog(@NonNull Context context, int themeResId) {
|
||||
super(context, R.style.SelectiveDialog);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
protected BottomShareDialog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
|
||||
super(context, R.style.SelectiveDialog);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.dialog_share);
|
||||
initView();
|
||||
|
||||
Window window = this.getWindow();
|
||||
window.setGravity(Gravity.BOTTOM);
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
params.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
window.setAttributes(params);
|
||||
}
|
||||
|
||||
private void initView(){
|
||||
mErView = findViewById(R.id.share_er);
|
||||
mWeixinView = findViewById(R.id.share_weixin);
|
||||
mPengView = findViewById(R.id.share_peng);
|
||||
mQQView = findViewById(R.id.share_qq);
|
||||
mKongView = findViewById(R.id.share_kong);
|
||||
mWeiboView = findViewById(R.id.share_weibo);
|
||||
mLinkView = findViewById(R.id.share_link);
|
||||
mErView.setOnClickListener(this);
|
||||
mWeixinView.setOnClickListener(this);
|
||||
mPengView.setOnClickListener(this);
|
||||
mQQView.setOnClickListener(this);
|
||||
mKongView.setOnClickListener(this);
|
||||
mWeiboView.setOnClickListener(this);
|
||||
mLinkView.setOnClickListener(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class BottomVideoDialog extends Dialog implements View.OnClickListener, Serializable {
|
||||
private Context mContext;
|
||||
private ImageView mVideoView;
|
||||
private ImageView mCancelButton;
|
||||
private TextView mVideoTag;
|
||||
private OnButtonClickedListener mListener;
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if(mVideoView == view){
|
||||
if(null != mListener){
|
||||
mListener.onVideoClicked(view);
|
||||
}
|
||||
}else if(mCancelButton == view){
|
||||
if(null != mListener){
|
||||
mListener.onCancelClicked(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnButtonClickedListener{
|
||||
void onVideoClicked(View view);
|
||||
void onCancelClicked(View view);
|
||||
}
|
||||
|
||||
public void setOnButtonClickedListener(OnButtonClickedListener listener){
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public BottomVideoDialog(@NonNull Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public BottomVideoDialog(@NonNull Context context, int themeResId) {
|
||||
super(context, R.style.SelectiveDialog);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
protected BottomVideoDialog(@NonNull Context context, boolean cancelable, @Nullable DialogInterface.OnCancelListener cancelListener) {
|
||||
super(context, R.style.SelectiveDialog);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.dialog_bottom_video);
|
||||
initView();
|
||||
|
||||
Window window = this.getWindow();
|
||||
window.setGravity(Gravity.BOTTOM);
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
params.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
window.setAttributes(params);
|
||||
}
|
||||
|
||||
private void initView(){
|
||||
mVideoView = findViewById(R.id.bottom_video_logo);
|
||||
mVideoTag = findViewById(R.id.bottom_video_text);
|
||||
mCancelButton = findViewById(R.id.bottom_video_cancel);
|
||||
mVideoView.setOnClickListener(this);
|
||||
mCancelButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
public void enableVideo(){
|
||||
mVideoView.setImageResource(R.drawable.ic_home_live2);
|
||||
mVideoTag.setText("关闭接单");
|
||||
mVideoTag.setTextColor(Color.parseColor("#FF3352"));
|
||||
}
|
||||
|
||||
public void disableVideo(){
|
||||
mVideoView.setImageResource(R.drawable.ic_home_live1);
|
||||
mVideoTag.setText("开始接单");
|
||||
mVideoTag.setTextColor(mContext.getResources().getColor(R.color.bottomTabTextColor));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
public class DialogGiftBean {
|
||||
private int id;
|
||||
private String effect;
|
||||
private String name;
|
||||
private int price;
|
||||
private String img;
|
||||
private int isSelect; //1选中
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(int price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public String getImg() {
|
||||
return img;
|
||||
}
|
||||
|
||||
public void setImg(String img) {
|
||||
this.img = img;
|
||||
}
|
||||
|
||||
public int getIsSelect() {
|
||||
return isSelect;
|
||||
}
|
||||
|
||||
public void setIsSelect(int isSelect) {
|
||||
this.isSelect = isSelect;
|
||||
}
|
||||
|
||||
public String getEffect() {
|
||||
return effect;
|
||||
}
|
||||
|
||||
public void setEffect(String effect) {
|
||||
this.effect = effect;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GiftListBean {
|
||||
private List<DialogGiftBean> gift_list = new ArrayList<>();
|
||||
private int coin;
|
||||
|
||||
public List<DialogGiftBean> getGift_list() {
|
||||
return gift_list;
|
||||
}
|
||||
|
||||
public void setGift_list(List<DialogGiftBean> gift_list) {
|
||||
this.gift_list = gift_list;
|
||||
}
|
||||
|
||||
public int getCoin() {
|
||||
return coin;
|
||||
}
|
||||
|
||||
public void setCoin(int coin) {
|
||||
this.coin = coin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcelable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class GiftPagesManager {
|
||||
private ViewPager mAdvPager = null;
|
||||
private ViewGroup mGroup = null;
|
||||
private List<View> mGiftView = new ArrayList<>();
|
||||
private Context mContext = null;
|
||||
private AtomicInteger mWhat = new AtomicInteger(0);
|
||||
private boolean isContinue = true;
|
||||
private List<DialogGiftBean> mTotalGiftBean = new ArrayList<>();
|
||||
private ImageView[] mImageViews = null;
|
||||
private List<DialogGiftBean>[] mPageGiftBean = null;
|
||||
private OnGiftItemClickedListener mGiftListener;
|
||||
private List<GiftRecycleAdapter> mAdapters;
|
||||
|
||||
public interface OnGiftItemClickedListener{
|
||||
void onGiftClicked(DialogGiftBean bean);
|
||||
}
|
||||
|
||||
public void setOnGiftItemClickedListener(OnGiftItemClickedListener listener){
|
||||
mGiftListener = listener;
|
||||
}
|
||||
|
||||
public GiftPagesManager(ViewPager advPager, ViewGroup group, Context context){
|
||||
this.mAdvPager = advPager;
|
||||
this.mContext = context;
|
||||
this.mGroup = group;
|
||||
}
|
||||
|
||||
public void addGiftBean(List<DialogGiftBean> giftBeans){
|
||||
mTotalGiftBean.addAll(giftBeans);
|
||||
}
|
||||
|
||||
|
||||
private void makeGiftView(){
|
||||
if(0 == mTotalGiftBean.size()){
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<DialogGiftBean> iterator = mTotalGiftBean.iterator();
|
||||
int count = 0;
|
||||
int groupId = 0;
|
||||
mPageGiftBean = new List[mTotalGiftBean.size() % 10 == 0 ? mTotalGiftBean.size() / 10 : mTotalGiftBean.size() / 10 + 1];
|
||||
mAdapters = new ArrayList<>();
|
||||
GiftRecycleAdapter adapter = new GiftRecycleAdapter(mContext);
|
||||
mAdapters.add(adapter);
|
||||
initView(adapter, groupId);
|
||||
while(iterator.hasNext()){
|
||||
DialogGiftBean bean = iterator.next();
|
||||
if(count >= 10){
|
||||
count = 0;
|
||||
groupId ++;
|
||||
adapter = new GiftRecycleAdapter(mContext);
|
||||
mAdapters.add(adapter);
|
||||
initView(adapter, groupId);
|
||||
}
|
||||
|
||||
mPageGiftBean[groupId].add(bean);
|
||||
adapter.notifyDataSetChanged();
|
||||
count ++;
|
||||
}
|
||||
}
|
||||
|
||||
private void initView(final GiftRecycleAdapter adapter, final int groupId){
|
||||
View giftView = View.inflate(mContext, R.layout.item_gift_board, null);
|
||||
mGiftView.add(giftView);
|
||||
RecyclerView board = giftView.findViewById(R.id.gift_recycler_view);
|
||||
mPageGiftBean[groupId] = new ArrayList<>();
|
||||
adapter.setGiftList(mPageGiftBean[groupId]);
|
||||
adapter.setOnGiftClickListener(new GiftRecycleAdapter.OnGiftClickListener() {
|
||||
@Override
|
||||
public void onClicked(int position) {
|
||||
if(null != mGiftListener){
|
||||
mGiftListener.onGiftClicked(mPageGiftBean[groupId].get(position));
|
||||
int size = mTotalGiftBean.size() % 10 == 0 ? mTotalGiftBean.size() / 10 : mTotalGiftBean.size() / 10 + 1;
|
||||
for (int i = 0; i < size; i++) {
|
||||
mAdapters.get(i).setSelect(-1);
|
||||
mAdapters.get(i).notifyDataSetChanged();
|
||||
}
|
||||
|
||||
adapter.setSelect(position);
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
board.setAdapter(adapter);
|
||||
// board.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.HORIZONTAL));
|
||||
board.setLayoutManager(new GridLayoutManager(mContext, 5));
|
||||
}
|
||||
|
||||
public void manage(){
|
||||
makeGiftView();
|
||||
// int size = mTotalGiftBean.size() % 10 == 0 ? mTotalGiftBean.size() / 10 : mTotalGiftBean.size() / 10 + 1;
|
||||
// mImageViews = new ImageView[size];
|
||||
// for (int i = 0; i < size; i++) {
|
||||
// ImageView imageView = new ImageView(mContext);
|
||||
// LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(10, 10);
|
||||
// params.setMargins(12, 0, 0, 0);
|
||||
// imageView.setLayoutParams(params);
|
||||
// imageView.setPadding(UiUtils.dip2px(mContext, 22), 12, UiUtils.dip2px(mContext, 22), 12);
|
||||
// mImageViews[i] = imageView;
|
||||
// if (i == 0) {
|
||||
// mImageViews[i]
|
||||
// .setBackgroundResource(R.drawable.shape_banner_index_focus);
|
||||
// } else {
|
||||
// mImageViews[i]
|
||||
// .setBackgroundResource(R.drawable.shape_banner_index_inactived);
|
||||
// }
|
||||
//
|
||||
// mGroup.addView(mImageViews[i]);
|
||||
// }
|
||||
|
||||
mAdvPager.setAdapter(new AdvAdapter(mGiftView));
|
||||
mAdvPager.setOnPageChangeListener(new GuidePageChangeListener());
|
||||
|
||||
// mAdvPager.setOnTouchListener(new View.OnTouchListener() {
|
||||
//
|
||||
// @Override
|
||||
// public boolean onTouch(View v, MotionEvent event) {
|
||||
// switch (event.getAction()) {
|
||||
// case MotionEvent.ACTION_DOWN:
|
||||
// case MotionEvent.ACTION_MOVE:
|
||||
// isContinue = false;
|
||||
// break;
|
||||
// case MotionEvent.ACTION_UP:
|
||||
// isContinue = true;
|
||||
//
|
||||
// break;
|
||||
// default:
|
||||
// isContinue = true;
|
||||
// break;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// new Thread(new Runnable() {
|
||||
//
|
||||
// @Override
|
||||
// public void run() {
|
||||
// while (true) {
|
||||
// if (isContinue) {
|
||||
// viewHandler.sendEmptyMessage(mWhat.get());
|
||||
// whatOption();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }).start();
|
||||
}
|
||||
|
||||
// private void whatOption() {
|
||||
// mWhat.incrementAndGet();
|
||||
// if (mWhat.get() > mImageViews.length - 1) {
|
||||
// mWhat.getAndAdd(-4);
|
||||
// }
|
||||
// try {
|
||||
// Thread.sleep(5000);
|
||||
// } catch (InterruptedException e) {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private final Handler viewHandler = new Handler() {
|
||||
//
|
||||
// @Override
|
||||
// public void handleMessage(Message msg) {
|
||||
// mAdvPager.setCurrentItem(msg.what);
|
||||
// super.handleMessage(msg);
|
||||
// }
|
||||
//
|
||||
// };
|
||||
|
||||
private final class GuidePageChangeListener implements ViewPager.OnPageChangeListener {
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int arg0) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int arg0, float arg1, int arg2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int arg0) {
|
||||
// mWhat.getAndSet(arg0);
|
||||
// for (int i = 0; i < mImageViews.length; i++) {
|
||||
// mImageViews[arg0]
|
||||
// .setBackgroundResource(R.drawable.shape_banner_index_focus);
|
||||
// if (arg0 != i) {
|
||||
// mImageViews[i]
|
||||
// .setBackgroundResource(R.drawable.shape_banner_index_inactived);
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class AdvAdapter extends PagerAdapter {
|
||||
private List<View> views = null;
|
||||
|
||||
public AdvAdapter(List<View> views) {
|
||||
this.views = views;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(View arg0, int arg1, Object arg2) {
|
||||
((ViewPager) arg0).removeView(views.get(arg1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpdate(View arg0) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return views.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(View arg0, int arg1) {
|
||||
((ViewPager) arg0).addView(views.get(arg1), 0);
|
||||
return views.get(arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(View arg0, Object arg1) {
|
||||
return arg0 == arg1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreState(Parcelable arg0, ClassLoader arg1) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable saveState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startUpdate(View arg0) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GiftRecycleAdapter extends RecyclerView.Adapter {
|
||||
// public final static String IMAGE_URL = "http://xqasset.whnuanbeinikj.cn/";
|
||||
public final static String IMAGE_URL = "http://xqasset.whnuanbeinikj.cn/";
|
||||
private List<DialogGiftBean> mGiftBeanList = new ArrayList<>();
|
||||
private Context mContext;
|
||||
private OnGiftClickListener mListener;
|
||||
private int selectPosition = -1;
|
||||
|
||||
public interface OnGiftClickListener{
|
||||
void onClicked(int position);
|
||||
}
|
||||
|
||||
public void setOnGiftClickListener(OnGiftClickListener listener){
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public GiftRecycleAdapter(Context context){
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void setGiftList(List<DialogGiftBean> giftBeans){
|
||||
mGiftBeanList = giftBeans;
|
||||
}
|
||||
|
||||
public void setSelect(int position) {
|
||||
this.selectPosition = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(mContext).inflate(R.layout.item_gift, null);
|
||||
return new Holder(mContext, view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
|
||||
DialogGiftBean giftBean = mGiftBeanList.get(position);
|
||||
Holder dataHolder = (Holder) holder;
|
||||
dataHolder.mGiftLogo.setImageURI(IMAGE_URL + giftBean.getImg());
|
||||
dataHolder.mName.setText(giftBean.getName());
|
||||
dataHolder.mCost.setText(giftBean.getPrice() + "金币");
|
||||
|
||||
if (selectPosition == position) {
|
||||
// dataHolder.llItem.setBackgroundColor(Color.parseColor("#ff00ff"));
|
||||
dataHolder.llItem.setBackgroundResource(R.drawable.shape_gift_orange_bg);
|
||||
}else {
|
||||
dataHolder.llItem.setBackgroundResource(0);
|
||||
}
|
||||
|
||||
dataHolder.mThisView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mListener.onClicked(position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mGiftBeanList.size();
|
||||
}
|
||||
|
||||
public class Holder extends ViewHolder {
|
||||
SimpleDraweeView mGiftLogo;
|
||||
TextView mName;
|
||||
TextView mCost;
|
||||
View mThisView;
|
||||
LinearLayout llItem;
|
||||
|
||||
public Holder(Context context, View itemView){
|
||||
super(context, itemView);
|
||||
mGiftLogo = itemView.findViewById(R.id.gift_photo);
|
||||
mName = itemView.findViewById(R.id.gift_name);
|
||||
mCost = itemView.findViewById(R.id.gift_cost);
|
||||
llItem = itemView.findViewById(R.id.ll_item);
|
||||
mThisView = itemView;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
* 先判断是否设定了mMaxHeight,如果设定了mMaxHeight,则直接使用mMaxHeight的值,
|
||||
* 如果没有设定mMaxHeight,则判断是否设定了mMaxRatio,如果设定了mMaxRatio的值
|
||||
* 则使用此值与屏幕高度的乘积作为最高高度
|
||||
*
|
||||
* @author Rick.Wang
|
||||
*/
|
||||
public class MaxHeightView extends LinearLayout {
|
||||
|
||||
private static final float DEFAULT_MAX_RATIO = 0.9f;
|
||||
private static final float DEFAULT_MAX_HEIGHT = -1f;
|
||||
|
||||
private Context mContext;
|
||||
private float mMaxHeight = DEFAULT_MAX_HEIGHT;//优先级高
|
||||
private float mMaxRatio = DEFAULT_MAX_RATIO;//优先级低
|
||||
|
||||
public MaxHeightView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public MaxHeightView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public MaxHeightView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
mContext = context;
|
||||
mMaxHeight = mMaxRatio * (float) getScreenHeight(mContext);
|
||||
}
|
||||
|
||||
public void setMaxHeight(float maxHeight) {
|
||||
mMaxHeight = maxHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
|
||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
|
||||
if (heightMode == MeasureSpec.EXACTLY) {
|
||||
heightSize = heightSize <= mMaxHeight ? heightSize : (int) mMaxHeight;
|
||||
}
|
||||
|
||||
if (heightMode == MeasureSpec.UNSPECIFIED) {
|
||||
heightSize = heightSize <= mMaxHeight ? heightSize : (int) mMaxHeight;
|
||||
}
|
||||
if (heightMode == MeasureSpec.AT_MOST) {
|
||||
heightSize = heightSize <= mMaxHeight ? heightSize : (int) mMaxHeight;
|
||||
}
|
||||
|
||||
int maxHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
|
||||
|
||||
super.onMeasure(widthMeasureSpec, maxHeightMeasureSpec);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取屏幕高度
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public static int getScreenHeight(Context context) {
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
return wm.getDefaultDisplay().getHeight();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.SystemClock;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextPaint;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
public class PrivateDialog {
|
||||
|
||||
|
||||
private static PrivateDialog instace;
|
||||
|
||||
public PrivateDialog() {
|
||||
|
||||
}
|
||||
|
||||
public static PrivateDialog getInstace() {
|
||||
|
||||
if (instace == null) {
|
||||
synchronized (PrivateDialog.class) {
|
||||
if (instace == null) {
|
||||
instace = new PrivateDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instace;
|
||||
}
|
||||
|
||||
|
||||
private String title = "用户协议和隐私协议提示";
|
||||
private String message;
|
||||
private String sure;
|
||||
private String cancle;
|
||||
|
||||
|
||||
private Dialog tipDialog;
|
||||
|
||||
private float clickTiem = 0;
|
||||
|
||||
|
||||
/**
|
||||
* desc: 提示隐私协议框
|
||||
*/
|
||||
public void showConnectDialog(Context mContext) {
|
||||
dismiss();
|
||||
if ((SystemClock.elapsedRealtime() - clickTiem) < 500) {
|
||||
return;
|
||||
}
|
||||
tipDialog = new AlertDialog.Builder(mContext).create();
|
||||
|
||||
tipDialog.setCanceledOnTouchOutside(false);
|
||||
|
||||
View view = LayoutInflater.from(mContext).inflate(R.layout.activity_privates_dialog, null, false);
|
||||
TextView tvTitle = view.findViewById(R.id.tv_sava_dialog_title);
|
||||
TextView dialogTxt = view.findViewById(R.id.tv_sava_dialog_message);
|
||||
TextView cancle = view.findViewById(R.id.tv_sava_dialog_cancel);
|
||||
Button sure = view.findViewById(R.id.tv_sava_dialog_confirg);
|
||||
String str = dialogTxt.getText().toString();
|
||||
SpannableStringBuilder tvProtocol = new SpannableStringBuilder(str);
|
||||
int start = str.indexOf("《用户协议》");
|
||||
int start1 = str.indexOf("《隐私政策》");
|
||||
|
||||
tvProtocol.setSpan(new CliclSpan(mContext,1),start,start+6, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
tvProtocol.setSpan(new CliclSpan(mContext,2),start1,start1+6,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
dialogTxt.setText(tvProtocol);
|
||||
dialogTxt.setHighlightColor(ContextCompat.getColor(dialogTxt.getContext(),android.R.color.transparent));
|
||||
dialogTxt.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
|
||||
|
||||
if (!this.title.isEmpty()) {
|
||||
tvTitle.setText(this.title);
|
||||
}
|
||||
if (!this.message.isEmpty()) {
|
||||
// tvMsg.setText(this.message);
|
||||
}
|
||||
if (!this.cancle.isEmpty()) {
|
||||
cancle.setText(this.cancle);
|
||||
}
|
||||
if (!this.sure.isEmpty()) {
|
||||
sure.setText(this.sure);
|
||||
}
|
||||
|
||||
|
||||
cancle.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
clickTiem = SystemClock.elapsedRealtime();
|
||||
tipDialog.dismiss();
|
||||
if (listener != null) {
|
||||
listener.cancleClick();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
sure.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
tipDialog.dismiss();
|
||||
if (listener != null) {
|
||||
listener.sureClick();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
tipDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||
@Override
|
||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_SEARCH)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false; //默认返回 false
|
||||
}
|
||||
}
|
||||
});
|
||||
tipDialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
tipDialog.show();
|
||||
tipDialog.setCancelable(false);
|
||||
tipDialog.getWindow().setContentView(view);
|
||||
//tipDialog.getWindow().setWindowAnimations(R.style.DialogBottom); // 添加动画
|
||||
|
||||
WindowManager windowManager = (WindowManager)
|
||||
mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||
Display display = windowManager.getDefaultDisplay();
|
||||
WindowManager.LayoutParams params = tipDialog.getWindow().getAttributes();
|
||||
params.width = (int) (display.getWidth() * 0.8);
|
||||
tipDialog.getWindow().setAttributes(params);
|
||||
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
if (tipDialog != null) {
|
||||
if (tipDialog.isShowing()) {
|
||||
tipDialog.dismiss();
|
||||
}
|
||||
tipDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnTipItemClickListener {
|
||||
void cancleClick();
|
||||
|
||||
void sureClick();
|
||||
|
||||
void userClick();
|
||||
|
||||
void termsClick();
|
||||
}
|
||||
|
||||
private OnTipItemClickListener listener;
|
||||
|
||||
public PrivateDialog setOnTipItemClickListener(OnTipItemClickListener listener) {
|
||||
this.listener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public PrivateDialog title(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PrivateDialog message(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PrivateDialog sure(String sure) {
|
||||
this.sure = sure;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PrivateDialog cancle(String cancle) {
|
||||
this.cancle = cancle;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public void create(Context mContext) {
|
||||
showConnectDialog(mContext);
|
||||
|
||||
}
|
||||
class CliclSpan extends ClickableSpan {
|
||||
Context mContext;
|
||||
Integer mNum;
|
||||
public CliclSpan() {
|
||||
super();
|
||||
}
|
||||
public CliclSpan(Context context,Integer num) {
|
||||
mContext = context;
|
||||
mNum = num;
|
||||
}
|
||||
@Override
|
||||
public void onClick(@NonNull View widget) {
|
||||
if (listener != null) {
|
||||
if(mNum == 1){
|
||||
listener.userClick();
|
||||
}else {
|
||||
listener.termsClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDrawState(@NonNull TextPaint ds) {
|
||||
ds.setColor(ContextCompat.getColor(mContext,R.color.mainColors));
|
||||
ds.setUnderlineText(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.SystemClock;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextPaint;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
public class PrivateDialogs {
|
||||
|
||||
|
||||
|
||||
private static PrivateDialogs instace;
|
||||
|
||||
public PrivateDialogs() {
|
||||
|
||||
}
|
||||
|
||||
public static PrivateDialogs getInstace() {
|
||||
|
||||
if (instace == null) {
|
||||
synchronized (PrivateDialogs.class) {
|
||||
if (instace == null) {
|
||||
instace = new PrivateDialogs();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instace;
|
||||
}
|
||||
|
||||
|
||||
private String title = "温馨提示";
|
||||
private String message;
|
||||
private String sure;
|
||||
private String cancle;
|
||||
|
||||
|
||||
private Dialog tipDialog;
|
||||
|
||||
private float clickTiem = 0;
|
||||
|
||||
|
||||
/**
|
||||
* desc: 提示隐私协议框
|
||||
*/
|
||||
public void showConnectDialog(Context mContext) {
|
||||
dismiss();
|
||||
if ((SystemClock.elapsedRealtime() - clickTiem) < 500) {
|
||||
return;
|
||||
}
|
||||
tipDialog = new AlertDialog.Builder(mContext).create();
|
||||
|
||||
tipDialog.setCanceledOnTouchOutside(false);
|
||||
|
||||
View view = LayoutInflater.from(mContext).inflate(R.layout.activity_privates_dialogs, null, false);
|
||||
TextView tvTitle = view.findViewById(R.id.tv_sava_dialog_title);
|
||||
TextView dialogTxt = view.findViewById(R.id.tv_sava_dialog_message);
|
||||
Button cancle = view.findViewById(R.id.tv_sava_dialog_cancel);
|
||||
Button sure = view.findViewById(R.id.tv_sava_dialog_confirg);
|
||||
SpannableStringBuilder tvProtocol = new SpannableStringBuilder(dialogTxt.getText().toString());
|
||||
tvProtocol.setSpan(new PrivateDialogs.CliclSpan(mContext,1),3,11, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
tvProtocol.setSpan(new PrivateDialogs.CliclSpan(mContext,2),12,20,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
dialogTxt.setText(tvProtocol);
|
||||
dialogTxt.setHighlightColor(ContextCompat.getColor(dialogTxt.getContext(),android.R.color.transparent));
|
||||
dialogTxt.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
|
||||
if (!this.title.isEmpty()) {
|
||||
tvTitle.setText(this.title);
|
||||
}
|
||||
if (!this.message.isEmpty()) {
|
||||
// tvMsg.setText(this.message);
|
||||
}
|
||||
if (!this.cancle.isEmpty()) {
|
||||
cancle.setText(this.cancle);
|
||||
}
|
||||
if (!this.sure.isEmpty()) {
|
||||
sure.setText(this.sure);
|
||||
}
|
||||
|
||||
|
||||
cancle.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
clickTiem = SystemClock.elapsedRealtime();
|
||||
tipDialog.dismiss();
|
||||
if (listener != null) {
|
||||
listener.cancleClick();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
sure.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
tipDialog.dismiss();
|
||||
if (listener != null) {
|
||||
listener.sureClick();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
tipDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||
@Override
|
||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_SEARCH)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false; //默认返回 false
|
||||
}
|
||||
}
|
||||
});
|
||||
tipDialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
tipDialog.show();
|
||||
tipDialog.setCancelable(false);
|
||||
tipDialog.getWindow().setContentView(view);
|
||||
//tipDialog.getWindow().setWindowAnimations(R.style.DialogBottom); // 添加动画
|
||||
|
||||
WindowManager windowManager = (WindowManager)
|
||||
mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||
Display display = windowManager.getDefaultDisplay();
|
||||
WindowManager.LayoutParams params = tipDialog.getWindow().getAttributes();
|
||||
params.width = (int) (display.getWidth() * 0.8);
|
||||
tipDialog.getWindow().setAttributes(params);
|
||||
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
if (tipDialog != null) {
|
||||
if (tipDialog.isShowing()) {
|
||||
tipDialog.dismiss();
|
||||
}
|
||||
tipDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnTipItemClickListener {
|
||||
void cancleClick();
|
||||
|
||||
void sureClick();
|
||||
|
||||
void userClick();
|
||||
|
||||
void termsClick();
|
||||
}
|
||||
|
||||
private PrivateDialogs.OnTipItemClickListener listener;
|
||||
|
||||
public PrivateDialogs setOnTipItemClickListener(PrivateDialogs.OnTipItemClickListener listener) {
|
||||
this.listener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public PrivateDialogs title(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PrivateDialogs message(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PrivateDialogs sure(String sure) {
|
||||
this.sure = sure;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PrivateDialogs cancle(String cancle) {
|
||||
this.cancle = cancle;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public void create(Context mContext) {
|
||||
showConnectDialog(mContext);
|
||||
|
||||
}
|
||||
class CliclSpan extends ClickableSpan {
|
||||
Context mContext;
|
||||
Integer mNum;
|
||||
public CliclSpan() {
|
||||
super();
|
||||
}
|
||||
public CliclSpan(Context context,Integer num) {
|
||||
mContext = context;
|
||||
mNum = num;
|
||||
}
|
||||
@Override
|
||||
public void onClick(@NonNull View widget) {
|
||||
if (listener != null) {
|
||||
if(mNum == 1){
|
||||
listener.userClick();
|
||||
}else {
|
||||
listener.termsClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDrawState(@NonNull TextPaint ds) {
|
||||
ds.setColor(ContextCompat.getColor(mContext,R.color.mainColors));
|
||||
ds.setUnderlineText(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
269
uikit/src/main/java/com/fengliyan/uikit/dialog/ViewHolder.java
Normal file
269
uikit/src/main/java/com/fengliyan/uikit/dialog/ViewHolder.java
Normal file
@@ -0,0 +1,269 @@
|
||||
package com.fengliyan.uikit.dialog;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.text.util.Linkify;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RatingBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder{
|
||||
private SparseArray<View> mViews;
|
||||
private View mConvertView;
|
||||
private Context mContext;
|
||||
|
||||
public ViewHolder(Context context, View itemView)
|
||||
{
|
||||
super(itemView);
|
||||
mContext = context;
|
||||
mConvertView = itemView;
|
||||
mViews = new SparseArray<View>();
|
||||
}
|
||||
|
||||
|
||||
public static ViewHolder createViewHolder(Context context, View itemView)
|
||||
{
|
||||
ViewHolder holder = new ViewHolder(context, itemView);
|
||||
return holder;
|
||||
}
|
||||
|
||||
public static ViewHolder createViewHolder(Context context,
|
||||
ViewGroup parent, int layoutId)
|
||||
{
|
||||
View itemView = LayoutInflater.from(context).inflate(layoutId, parent,
|
||||
false);
|
||||
ViewHolder holder = new ViewHolder(context, itemView);
|
||||
return holder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过viewId获取控件
|
||||
*
|
||||
* @param viewId
|
||||
* @return
|
||||
*/
|
||||
public <T extends View> T getView(int viewId)
|
||||
{
|
||||
View view = mViews.get(viewId);
|
||||
if (view == null)
|
||||
{
|
||||
view = mConvertView.findViewById(viewId);
|
||||
mViews.put(viewId, view);
|
||||
}
|
||||
return (T) view;
|
||||
}
|
||||
|
||||
public View getConvertView()
|
||||
{
|
||||
return mConvertView;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/****以下为辅助方法*****/
|
||||
|
||||
/**
|
||||
* 设置TextView的值
|
||||
*
|
||||
* @param viewId
|
||||
* @param text
|
||||
* @return
|
||||
*/
|
||||
public ViewHolder setText(int viewId, String text)
|
||||
{
|
||||
TextView tv = getView(viewId);
|
||||
tv.setText(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setImageResource(int viewId, int resId)
|
||||
{
|
||||
ImageView view = getView(viewId);
|
||||
view.setImageResource(resId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setImageBitmap(int viewId, Bitmap bitmap)
|
||||
{
|
||||
ImageView view = getView(viewId);
|
||||
view.setImageBitmap(bitmap);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setImageDrawable(int viewId, Drawable drawable)
|
||||
{
|
||||
ImageView view = getView(viewId);
|
||||
view.setImageDrawable(drawable);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setBackgroundColor(int viewId, int color)
|
||||
{
|
||||
View view = getView(viewId);
|
||||
view.setBackgroundColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setBackgroundRes(int viewId, int backgroundRes)
|
||||
{
|
||||
View view = getView(viewId);
|
||||
view.setBackgroundResource(backgroundRes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setTextColor(int viewId, int textColor)
|
||||
{
|
||||
TextView view = getView(viewId);
|
||||
view.setTextColor(textColor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setTextColorRes(int viewId, int textColorRes)
|
||||
{
|
||||
TextView view = getView(viewId);
|
||||
view.setTextColor(mContext.getResources().getColor(textColorRes));
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public ViewHolder setAlpha(int viewId, float value)
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
|
||||
{
|
||||
getView(viewId).setAlpha(value);
|
||||
} else
|
||||
{
|
||||
// Pre-honeycomb hack to set Alpha value
|
||||
AlphaAnimation alpha = new AlphaAnimation(value, value);
|
||||
alpha.setDuration(0);
|
||||
alpha.setFillAfter(true);
|
||||
getView(viewId).startAnimation(alpha);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setVisible(int viewId, boolean visible)
|
||||
{
|
||||
View view = getView(viewId);
|
||||
view.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder linkify(int viewId)
|
||||
{
|
||||
TextView view = getView(viewId);
|
||||
Linkify.addLinks(view, Linkify.ALL);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setTypeface(Typeface typeface, int... viewIds)
|
||||
{
|
||||
for (int viewId : viewIds)
|
||||
{
|
||||
TextView view = getView(viewId);
|
||||
view.setTypeface(typeface);
|
||||
view.setPaintFlags(view.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setProgress(int viewId, int progress)
|
||||
{
|
||||
ProgressBar view = getView(viewId);
|
||||
view.setProgress(progress);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setProgress(int viewId, int progress, int max)
|
||||
{
|
||||
ProgressBar view = getView(viewId);
|
||||
view.setMax(max);
|
||||
view.setProgress(progress);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setMax(int viewId, int max)
|
||||
{
|
||||
ProgressBar view = getView(viewId);
|
||||
view.setMax(max);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setRating(int viewId, float rating)
|
||||
{
|
||||
RatingBar view = getView(viewId);
|
||||
view.setRating(rating);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setRating(int viewId, float rating, int max)
|
||||
{
|
||||
RatingBar view = getView(viewId);
|
||||
view.setMax(max);
|
||||
view.setRating(rating);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setTag(int viewId, Object tag)
|
||||
{
|
||||
View view = getView(viewId);
|
||||
view.setTag(tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setTag(int viewId, int key, Object tag)
|
||||
{
|
||||
View view = getView(viewId);
|
||||
view.setTag(key, tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setChecked(int viewId, boolean checked)
|
||||
{
|
||||
Checkable view = (Checkable) getView(viewId);
|
||||
view.setChecked(checked);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关于事件的
|
||||
*/
|
||||
public ViewHolder setOnClickListener(int viewId,
|
||||
View.OnClickListener listener)
|
||||
{
|
||||
View view = getView(viewId);
|
||||
view.setOnClickListener(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setOnTouchListener(int viewId,
|
||||
View.OnTouchListener listener)
|
||||
{
|
||||
View view = getView(viewId);
|
||||
view.setOnTouchListener(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewHolder setOnLongClickListener(int viewId,
|
||||
View.OnLongClickListener listener)
|
||||
{
|
||||
View view = getView(viewId);
|
||||
view.setOnLongClickListener(listener);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
package com.fengliyan.uikit.editor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
/**
|
||||
* Created by ChenQihong on 2016/12/15.
|
||||
*/
|
||||
|
||||
public class AutoDivisionEditText extends EditText {
|
||||
|
||||
private char divideCharacter = ' ';
|
||||
private int[] formatPattern = {4};
|
||||
private int maxLength = 24;
|
||||
private int[] divisionPattern;
|
||||
private boolean isMoneyType = false;
|
||||
|
||||
private View.OnFocusChangeListener extFocusListener = null;
|
||||
|
||||
private boolean divisionEnable = true;
|
||||
|
||||
public AutoDivisionEditText(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public AutoDivisionEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public AutoDivisionEditText(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// addTextChangedListener(mInputWatcher);
|
||||
// setInputType(InputType.TYPE_CLASS_PHONE);
|
||||
Divider u = new Divider();
|
||||
addTextChangedListener(u);
|
||||
setFilters(new InputFilter[]{u});
|
||||
divisionPattern = getDivisionPatter();
|
||||
final View.OnFocusChangeListener listener = getOnFocusChangeListener();
|
||||
super.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (listener != null) {
|
||||
listener.onFocusChange(v, hasFocus);
|
||||
}
|
||||
if (extFocusListener != null) {
|
||||
extFocusListener.onFocusChange(v, hasFocus);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public View.OnFocusChangeListener getExtFocusListener() {
|
||||
return extFocusListener;
|
||||
}
|
||||
|
||||
//
|
||||
//去掉拦截,放开某些自定义系统的调用
|
||||
// @Override
|
||||
// public void setOnFocusChangeListener(OnFocusChangeListener extFocusListener) {
|
||||
// throw new RuntimeException("Don't call setOnFocusChangeListener on ZlEditText. "
|
||||
// + "You probably want setExtFocusListener instead");
|
||||
// }
|
||||
|
||||
/**
|
||||
* 设置监听函数
|
||||
*
|
||||
* @param extFocusListener 监听函数
|
||||
*/
|
||||
public void setExtFocusListener(View.OnFocusChangeListener extFocusListener) {
|
||||
this.extFocusListener = extFocusListener;
|
||||
}
|
||||
|
||||
public boolean isDivisionEnable() {
|
||||
return divisionEnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否允许分割
|
||||
*
|
||||
* @param divisionEnable true 自动分隔
|
||||
*/
|
||||
public void setDivisionEnable(boolean divisionEnable) {
|
||||
this.divisionEnable = divisionEnable;
|
||||
}
|
||||
|
||||
public boolean isMoneyType() {
|
||||
return isMoneyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入类型为金额
|
||||
*
|
||||
* @param isMoneyType true 金额类型
|
||||
*/
|
||||
public void setMoneyType(boolean isMoneyType) {
|
||||
this.isMoneyType = isMoneyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符格式化模式,例如设置为{3,4,4},字符将要显示成138 3800 3800的形式
|
||||
*
|
||||
* @param formatPattern 分隔模式
|
||||
*/
|
||||
public void setFormatPattern(int maxLength, int[] formatPattern) {
|
||||
divisionEnable = true;
|
||||
this.maxLength = maxLength;
|
||||
this.formatPattern = formatPattern;
|
||||
divisionPattern = getDivisionPatter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分隔字符
|
||||
*/
|
||||
public char getDividerCharacter() {
|
||||
return divideCharacter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取允许输入的最大长度
|
||||
*/
|
||||
public int getMaxLength() {
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
public void setMaxLength(int maxLength) {
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置分隔字符
|
||||
*
|
||||
* @param dividerCharacter 分隔字符
|
||||
*/
|
||||
public void setDividerCharacter(char dividerCharacter) {
|
||||
this.divideCharacter = dividerCharacter;
|
||||
}
|
||||
|
||||
private CharSequence deleteDivideChar(CharSequence string) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int length = string.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (string.charAt(i) != divideCharacter) {
|
||||
builder.append(string.charAt(i));
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取去掉分隔符的内容
|
||||
*
|
||||
* @return 去掉了分隔符的内容
|
||||
*/
|
||||
public String getRealInputText() {
|
||||
if (divisionEnable) {
|
||||
return deleteDivideChar(getText()).toString();
|
||||
} else {
|
||||
return getText().toString();
|
||||
}
|
||||
}
|
||||
|
||||
private int[] getDivisionPatter() {
|
||||
int len = 0;
|
||||
int i = 0;
|
||||
while (len < maxLength) {
|
||||
if (i < formatPattern.length) {
|
||||
len += formatPattern[i];
|
||||
} else {
|
||||
len += formatPattern[formatPattern.length - 1];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
int[] arrayInt = new int[--i];
|
||||
arrayInt[0] = formatPattern[0];
|
||||
for (int j = 1; j < i; j++) {
|
||||
arrayInt[j] = arrayInt[j - 1];
|
||||
if (j < formatPattern.length) {
|
||||
arrayInt[j] += formatPattern[j] + 1;
|
||||
} else {
|
||||
arrayInt[j] += formatPattern[formatPattern.length - 1] + 1;
|
||||
}
|
||||
}
|
||||
return arrayInt;
|
||||
}
|
||||
|
||||
final class Divider implements TextWatcher, InputFilter {
|
||||
private int deleteDivider = 0;
|
||||
|
||||
public final void afterTextChanged(Editable source) {
|
||||
if (isMoneyType) {
|
||||
do {
|
||||
if (source.length() == 1 && source.charAt(0) == '.') {
|
||||
source.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
if (source.length() == 2 && source.charAt(0) == '0' && !source.toString().equals("0.")) {
|
||||
source.delete(0, 1);
|
||||
}
|
||||
|
||||
int dotPos = -1;
|
||||
int len = source.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (source.charAt(i) == '.') {
|
||||
dotPos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//金额类型最多输入两位小数
|
||||
if (dotPos != -1 && (dotPos + 3) < len) {
|
||||
source.delete(dotPos + 3, source.length());
|
||||
}
|
||||
} while (false);
|
||||
}
|
||||
|
||||
if (!divisionEnable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.deleteDivider > 1) {
|
||||
int tmp = this.deleteDivider;
|
||||
this.deleteDivider = 0;
|
||||
source.delete(tmp - 1, tmp);
|
||||
}
|
||||
int j = 0;
|
||||
for (int i = 0; i < source.length(); i++) {
|
||||
char character = source.charAt(i);
|
||||
if (j > divisionPattern.length - 1) {
|
||||
j = divisionPattern.length - 1;
|
||||
}
|
||||
if (i == divisionPattern[j]) {
|
||||
if (j < divisionPattern.length - 1) {
|
||||
j++;
|
||||
}
|
||||
if (character != divideCharacter) {
|
||||
source.insert(i, String.valueOf(divideCharacter));
|
||||
}
|
||||
} else if (character == divideCharacter) {
|
||||
source.delete(i, i + 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void beforeTextChanged(CharSequence paramCharSequence, int paramInt1, int paramInt2, int paramInt3) {
|
||||
}
|
||||
|
||||
public final void onTextChanged(CharSequence paramCharSequence, int paramInt1, int paramInt2, int paramInt3) {
|
||||
}
|
||||
|
||||
public final CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
|
||||
if (!divisionEnable) {
|
||||
return source;
|
||||
}
|
||||
|
||||
if (deleteDivideChar(
|
||||
new SpannableStringBuilder(dest).replace(dstart, dend, source, start, end).toString()).length() > maxLength) {
|
||||
return "";
|
||||
}
|
||||
SpannableStringBuilder result = new SpannableStringBuilder(source);
|
||||
int[] arrayOfInt = divisionPattern;
|
||||
int dLength = dend - dstart;
|
||||
// if(dest.length()<=dstart)
|
||||
// return result;
|
||||
|
||||
for (int i = 0; i < arrayOfInt.length; i++) {
|
||||
if ((source.length() == 0) && (dstart == arrayOfInt[i]) && (dest.charAt(dstart) == divideCharacter)) {
|
||||
this.deleteDivider = arrayOfInt[i];
|
||||
}
|
||||
int j;
|
||||
if ((dstart - dLength <= arrayOfInt[i])
|
||||
&& (dstart + end - dLength >= arrayOfInt[i])
|
||||
&& (((j = arrayOfInt[i] - dstart) == end) || ((j >= 0) && (j < end) && (result.charAt(j) != divideCharacter)))) {
|
||||
result.insert(j, String.valueOf(divideCharacter));
|
||||
end++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
179
uikit/src/main/java/com/fengliyan/uikit/editor/CaiEditText.java
Normal file
179
uikit/src/main/java/com/fengliyan/uikit/editor/CaiEditText.java
Normal file
@@ -0,0 +1,179 @@
|
||||
package com.fengliyan.uikit.editor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
/**
|
||||
* Created by ChenQihong on 2016/12/14.
|
||||
*/
|
||||
|
||||
public class CaiEditText extends LinearLayout implements View.OnClickListener {
|
||||
public static final int EDIT_TYPE_NONE = 0x00000000;
|
||||
public static final int EDIT_TYPE_TEXT = 0x00000001;
|
||||
public static final int EDIT_TYPE_PHONE_NUM = 0x00000003;
|
||||
public static final int EDIT_TYPE_NUM_PASSWORD = 0x00000012;
|
||||
private AutoDivisionEditText mEditText;
|
||||
private ImageView mFunctionalImage;
|
||||
private ImageView mInputLogo;
|
||||
private int mInputType = EDIT_TYPE_NONE;
|
||||
private OnRightFunctionButtonClickListener mRightClick;
|
||||
private TextWatcher mWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
|
||||
if (!mEditText.isEnabled()) {
|
||||
mFunctionalImage.setVisibility(View.INVISIBLE);
|
||||
return;
|
||||
}
|
||||
if (editable.length() > 0 && mEditText.isFocused()) {
|
||||
mFunctionalImage.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mFunctionalImage.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public interface OnRightFunctionButtonClickListener {
|
||||
void onClick(View v);
|
||||
}
|
||||
|
||||
public CaiEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.cai_edit_text, this, true);
|
||||
mEditText = (AutoDivisionEditText) findViewById(R.id.cai_edit_text);
|
||||
mFunctionalImage = (ImageView) findViewById(R.id.cai_function_image);
|
||||
mInputLogo = (ImageView) findViewById(R.id.cai_input_logo);
|
||||
mFunctionalImage.setOnClickListener(this);
|
||||
|
||||
mEditText.setDivisionEnable(false);
|
||||
mEditText.addTextChangedListener(mWatcher);
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CaiEditText);
|
||||
int n = a.getIndexCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
int attr = a.getIndex(i);
|
||||
if (attr == R.styleable.CaiEditText_edit_type) {
|
||||
mInputType = a.getInt(attr, EditorInfo.TYPE_NULL);
|
||||
} else if (attr == R.styleable.CaiEditText_hint) {
|
||||
String hint = a.getString(attr);
|
||||
mEditText.setHint(hint);
|
||||
} else if (attr == R.styleable.CaiEditText_left_img) {
|
||||
int resId = a.getResourceId(attr, 0);
|
||||
if (0 != resId) {
|
||||
mInputLogo.setImageResource(resId);
|
||||
}
|
||||
} else if (attr == R.styleable.CaiEditText_right_img) {
|
||||
int resId = a.getResourceId(attr, 0);
|
||||
if (0 != resId) {
|
||||
mFunctionalImage.setImageResource(resId);
|
||||
}
|
||||
} else if (attr == R.styleable.CaiEditText_left_img_enable) {
|
||||
boolean enable = a.getBoolean(attr, true);
|
||||
if (enable) {
|
||||
mInputLogo.setVisibility(VISIBLE);
|
||||
} else {
|
||||
mInputLogo.setVisibility(GONE);
|
||||
}
|
||||
} else if (attr == R.styleable.CaiEditText_right_img_enable) {
|
||||
boolean enable = a.getBoolean(attr, true);
|
||||
if (enable) {
|
||||
mFunctionalImage.setVisibility(VISIBLE);
|
||||
} else {
|
||||
mFunctionalImage.setVisibility(GONE);
|
||||
}
|
||||
} else if (attr == R.styleable.CaiEditText_hint_color) {
|
||||
int colorId = a.getColor(R.styleable.CaiEditText_hint_color, getResources().getColor(R.color.cp_color_gray));
|
||||
// ColorStateList colorId = a.getColorStateList(R.styleable.CaiEditText_hint_color);
|
||||
// int colorId = a.getResourceId(attr, 0);
|
||||
// if (0 != colorId) {
|
||||
mEditText.setHintTextColor(colorId);
|
||||
// }
|
||||
} else if (attr == R.styleable.CaiEditText_gravity_center) {
|
||||
boolean enable = a.getBoolean(attr, false);
|
||||
if (enable) {
|
||||
mEditText.setGravity(Gravity.CENTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mInputType == EDIT_TYPE_PHONE_NUM) {
|
||||
mEditText.setDivisionEnable(true);
|
||||
int[] newFormatType = {3, 4, 4};
|
||||
mEditText.setFormatPattern(11, newFormatType);
|
||||
//mEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(13)});
|
||||
} else {
|
||||
|
||||
mEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(20)});
|
||||
}
|
||||
mEditText.setInputType(mInputType);
|
||||
}
|
||||
|
||||
public int getInputType() {
|
||||
return mInputType;
|
||||
}
|
||||
|
||||
public void setInputType(int inputType) {
|
||||
mEditText.setInputType(inputType);
|
||||
this.mInputType = inputType;
|
||||
}
|
||||
|
||||
public void setDivisionEnable(boolean enable) {
|
||||
mEditText.setDivisionEnable(enable);
|
||||
}
|
||||
|
||||
public void setRightButtonFunction(OnRightFunctionButtonClickListener rightClick) {
|
||||
mRightClick = rightClick;
|
||||
}
|
||||
|
||||
public void setRightButtonImageById(int resid) {
|
||||
mFunctionalImage.setImageResource(resid);
|
||||
}
|
||||
|
||||
public AutoDivisionEditText getEditText() {
|
||||
return mEditText;
|
||||
}
|
||||
|
||||
public void setEditText(AutoDivisionEditText editText) {
|
||||
this.mEditText = editText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (view == mFunctionalImage && null != mRightClick) {
|
||||
mRightClick.onClick(view);
|
||||
}
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return mEditText.getRealInputText();
|
||||
}
|
||||
|
||||
public void setText(String message) {
|
||||
mEditText.setText(message);
|
||||
}
|
||||
|
||||
public void enable(boolean isEnable) {
|
||||
mEditText.setEnabled(isEnable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
|
||||
package com.fengliyan.uikit.emoji;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
/**
|
||||
* 用于把附件保存到文件系统中
|
||||
*/
|
||||
public class AttachmentStore {
|
||||
public static long copy(String srcPath, String dstPath) {
|
||||
if (TextUtils.isEmpty(srcPath) || TextUtils.isEmpty(dstPath)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
File source = new File(srcPath);
|
||||
if (!source.exists()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (srcPath.equals(dstPath)) {
|
||||
return source.length();
|
||||
}
|
||||
|
||||
FileChannel fcin = null;
|
||||
FileChannel fcout = null;
|
||||
try {
|
||||
fcin = new FileInputStream(source).getChannel();
|
||||
fcout = new FileOutputStream(create(dstPath)).getChannel();
|
||||
ByteBuffer tmpBuffer = ByteBuffer.allocateDirect(4096);
|
||||
while (fcin.read(tmpBuffer) != -1) {
|
||||
tmpBuffer.flip();
|
||||
fcout.write(tmpBuffer);
|
||||
tmpBuffer.clear();
|
||||
}
|
||||
return source.length();
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (fcin != null) {
|
||||
fcin.close();
|
||||
}
|
||||
if (fcout != null) {
|
||||
fcout.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static long getFileLength(String srcPath) {
|
||||
if (TextUtils.isEmpty(srcPath)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
File srcFile = new File(srcPath);
|
||||
if (!srcFile.exists()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return srcFile.length();
|
||||
}
|
||||
|
||||
public static long save(String path, String content) {
|
||||
return save(content.getBytes(), path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 把数据保存到文件系统中,并且返回其大小
|
||||
*
|
||||
* @param data
|
||||
* @param filePath
|
||||
* @return 如果保存失败, 则返回-1
|
||||
*/
|
||||
public static long save(byte[] data, String filePath) {
|
||||
if (TextUtils.isEmpty(filePath)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
File f = new File(filePath);
|
||||
if (f.getParentFile() == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!f.getParentFile().exists()) {// 如果不存在上级文件夹
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
try {
|
||||
f.createNewFile();
|
||||
FileOutputStream fout = new FileOutputStream(f);
|
||||
fout.write(data);
|
||||
fout.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
return f.length();
|
||||
}
|
||||
|
||||
public static boolean move(String srcFilePath, String dstFilePath) {
|
||||
if (TextUtils.isEmpty(srcFilePath) || TextUtils.isEmpty(dstFilePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File srcFile = new File(srcFilePath);
|
||||
if (!srcFile.exists() || !srcFile.isFile()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File dstFile = new File(dstFilePath);
|
||||
if (dstFile.getParentFile() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dstFile.getParentFile().exists()) {// 如果不存在上级文件夹
|
||||
dstFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
return srcFile.renameTo(dstFile);
|
||||
}
|
||||
|
||||
public static File create(String filePath) {
|
||||
if (TextUtils.isEmpty(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File f = new File(filePath);
|
||||
if (!f.getParentFile().exists()) {// 如果不存在上级文件夹
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
try {
|
||||
f.createNewFile();
|
||||
return f;
|
||||
} catch (IOException e) {
|
||||
if (f != null && f.exists()) {
|
||||
f.delete();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param is
|
||||
* @param filePath
|
||||
* @return 保存失败,返回-1
|
||||
*/
|
||||
public static long save(InputStream is, String filePath) {
|
||||
File f = new File(filePath);
|
||||
if (!f.getParentFile().exists()) {// 如果不存在上级文件夹
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
f.createNewFile();
|
||||
fos = new FileOutputStream(f);
|
||||
int read = 0;
|
||||
byte[] bytes = new byte[8091];
|
||||
while ((read = is.read(bytes)) != -1) {
|
||||
fos.write(bytes, 0, read);
|
||||
}
|
||||
return f.length();
|
||||
} catch (IOException e) {
|
||||
if (f != null && f.exists()) {
|
||||
f.delete();
|
||||
}
|
||||
|
||||
return -1;
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
if (fos != null) {
|
||||
fos.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把文件从文件系统中读取出来
|
||||
*
|
||||
* @param path
|
||||
* @return 如果无法读取, 则返回null
|
||||
*/
|
||||
public static byte[] load(String path) {
|
||||
try {
|
||||
File f = new File(path);
|
||||
int unread = (int) f.length();
|
||||
int read = 0;
|
||||
byte[] buf = new byte[unread]; // 读取文件长度
|
||||
FileInputStream fin = new FileInputStream(f);
|
||||
do {
|
||||
int count = fin.read(buf, read, unread);
|
||||
read += count;
|
||||
unread -= count;
|
||||
} while (unread != 0);
|
||||
fin.close();
|
||||
return buf;
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String loadAsString(String path) {
|
||||
if (isFileExist(path)) {
|
||||
byte[] content = load(path);
|
||||
return new String(content);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定路径文件
|
||||
*
|
||||
* @param path
|
||||
*/
|
||||
public static boolean delete(String path) {
|
||||
if (TextUtils.isEmpty(path)) {
|
||||
return false;
|
||||
}
|
||||
File f = new File(path);
|
||||
if (f.exists()) {
|
||||
f = renameOnDelete(f);
|
||||
return f.delete();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteOnExit(String path) {
|
||||
if (TextUtils.isEmpty(path)) {
|
||||
return;
|
||||
}
|
||||
File f = new File(path);
|
||||
if (f.exists()) {
|
||||
f.deleteOnExit();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean deleteDir(String path) {
|
||||
return deleteDir(path, true);
|
||||
}
|
||||
|
||||
private static boolean deleteDir(String path, boolean rename) {
|
||||
boolean success = true;
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
if (rename) {
|
||||
file = renameOnDelete(file);
|
||||
}
|
||||
|
||||
File[] list = file.listFiles();
|
||||
if (list != null) {
|
||||
int len = list.length;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (list[i].isDirectory()) {
|
||||
deleteDir(list[i].getPath(), false);
|
||||
} else {
|
||||
boolean ret = list[i].delete();
|
||||
if (!ret) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
if (success) {
|
||||
file.delete();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// rename before delete to avoid lingering filesystem lock of android
|
||||
private static File renameOnDelete(File file) {
|
||||
String tmpPath = file.getParent() + "/" + System.currentTimeMillis() + "_tmp";
|
||||
File tmpFile = new File(tmpPath);
|
||||
if (file.renameTo(tmpFile)) {
|
||||
return tmpFile;
|
||||
} else {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isFileExist(String path) {
|
||||
if (!TextUtils.isEmpty(path) && new File(path).exists()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean saveBitmap(Bitmap bitmap, String path, boolean recycle) {
|
||||
if (bitmap == null || TextUtils.isEmpty(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BufferedOutputStream bos = null;
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(path);
|
||||
bos = new BufferedOutputStream(fos);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos);
|
||||
return true;
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
return false;
|
||||
} finally {
|
||||
if (bos != null) {
|
||||
try {
|
||||
bos.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
if (recycle) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.fengliyan.uikit.emoji;
|
||||
|
||||
public interface IEmoticonCategoryChanged {
|
||||
void onCategoryChanged(int index);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.fengliyan.uikit.emoji;
|
||||
|
||||
public interface IEmoticonSelectedListener {
|
||||
void onEmojiSelected(String key);
|
||||
|
||||
void onStickerSelected(String categoryName, String stickerName);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.fengliyan.uikit.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.style.ImageSpan;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
public class ImageSpanAlignCenter extends ImageSpan {
|
||||
|
||||
private static final char[] ELLIPSIS_NORMAL = {'\u2026'};
|
||||
private static final char[] ELLIPSIS_TWO_DOTS = {'\u2025'};
|
||||
|
||||
public ImageSpanAlignCenter(Context context, int resourceId) {
|
||||
super(context, resourceId);
|
||||
}
|
||||
|
||||
public ImageSpanAlignCenter(Drawable d) {
|
||||
super(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize(Paint paint, CharSequence text,
|
||||
int start, int end,
|
||||
Paint.FontMetricsInt fm) {
|
||||
Drawable d = getCachedDrawable(paint);
|
||||
Rect rect = d.getBounds();
|
||||
|
||||
if (fm != null) {
|
||||
Paint.FontMetricsInt fontMetrics = new Paint.FontMetricsInt();
|
||||
paint.getFontMetricsInt(fontMetrics);
|
||||
|
||||
fm.ascent = fontMetrics.ascent;
|
||||
fm.descent = fontMetrics.descent;
|
||||
|
||||
fm.top = fontMetrics.top;
|
||||
fm.bottom = fontMetrics.bottom;
|
||||
}
|
||||
return rect.right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
|
||||
int bottom, Paint paint) {
|
||||
final String s = text.toString();
|
||||
String subS = s.substring(start, end);
|
||||
if (ELLIPSIS_NORMAL[0] == subS.charAt(0)
|
||||
|| ELLIPSIS_TWO_DOTS[0] == subS.charAt(0)) {
|
||||
canvas.save();
|
||||
canvas.drawText(subS, x, y, paint);
|
||||
canvas.restore();
|
||||
} else {
|
||||
Drawable d = getCachedDrawable(paint);
|
||||
canvas.save();
|
||||
int transY;
|
||||
Paint.FontMetricsInt fontMetrics = new Paint.FontMetricsInt();
|
||||
paint.getFontMetricsInt(fontMetrics);
|
||||
transY = y + fontMetrics.ascent;
|
||||
canvas.translate(x, transY);
|
||||
d.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable getCachedDrawable(Paint paint) {
|
||||
WeakReference<Drawable> wr = mDrawableRef;
|
||||
Drawable d = null;
|
||||
|
||||
if (wr != null)
|
||||
d = wr.get();
|
||||
|
||||
if (d == null) {
|
||||
d = getDrawable();
|
||||
d.setBounds(new Rect(0, 0, paint.getFontMetricsInt(null), paint.getFontMetricsInt(null)));
|
||||
mDrawableRef = new WeakReference<Drawable>(d);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
private WeakReference<Drawable> mDrawableRef;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.fengliyan.uikit.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import com.fengliyan.base.base.ContextHolder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class StickerCategory implements Serializable {
|
||||
private static final long serialVersionUID = -81692490861539040L;
|
||||
|
||||
private String name; // 贴纸包名
|
||||
private String title; // 显示的标题
|
||||
private boolean system; // 是否是系统内置表情
|
||||
private int order = 0; // 默认顺序
|
||||
|
||||
private transient List<StickerItem> stickers;
|
||||
|
||||
public StickerCategory(String name, String title, boolean system, int order) {
|
||||
this.title = title;
|
||||
this.name = name;
|
||||
this.system = system;
|
||||
this.order = order;
|
||||
|
||||
loadStickerData();
|
||||
}
|
||||
|
||||
public boolean system() {
|
||||
return system;
|
||||
}
|
||||
|
||||
public void setSystem(boolean system) {
|
||||
this.system = system;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<StickerItem> getStickers() {
|
||||
return stickers;
|
||||
}
|
||||
|
||||
public boolean hasStickers() {
|
||||
return stickers != null && stickers.size() > 0;
|
||||
}
|
||||
|
||||
public InputStream getCoverNormalInputStream(Context context) {
|
||||
String filename = name + "_s_normal.png";
|
||||
return makeFileInputStream(context, filename);
|
||||
}
|
||||
|
||||
public InputStream getCoverPressedInputStream(Context context) {
|
||||
String filename = name + "_s_pressed.png";
|
||||
return makeFileInputStream(context, filename);
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
if (stickers == null || stickers.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return stickers.size();
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
private InputStream makeFileInputStream(Context context, String filename) {
|
||||
try {
|
||||
if (system) {
|
||||
AssetManager assetManager = context.getResources().getAssets();
|
||||
String path = "sticker/" + filename;
|
||||
return assetManager.open(path);
|
||||
} else {
|
||||
// for future
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<StickerItem> loadStickerData() {
|
||||
List<StickerItem> stickers = new ArrayList<>();
|
||||
AssetManager assetManager = ContextHolder.getApplicationContext().getResources().getAssets();
|
||||
try {
|
||||
String[] files = assetManager.list("sticker/" + name);
|
||||
for (String file : files) {
|
||||
stickers.add(new StickerItem(name, file));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
this.stickers = stickers;
|
||||
return stickers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(o instanceof StickerCategory)) {
|
||||
return false;
|
||||
}
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
StickerCategory r = (StickerCategory) o;
|
||||
return r.getName().equals(getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.fengliyan.uikit.emoji;
|
||||
|
||||
public class StickerItem {
|
||||
private String category;//类别名
|
||||
private String name;
|
||||
|
||||
public StickerItem(String category, String name) {
|
||||
this.category = category;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getIdentifier() {
|
||||
return category + "/" + name;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o != null && o instanceof StickerItem) {
|
||||
StickerItem item = (StickerItem) o;
|
||||
return item.getCategory().equals(category) && item.getName().equals(name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.fengliyan.uikit.emoji.manager;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.emoji.view.EmoticonView;
|
||||
|
||||
public class EmojiAdapter extends BaseAdapter {
|
||||
|
||||
private Context context;
|
||||
|
||||
private int startIndex;
|
||||
|
||||
public EmojiAdapter(Context mContext, int startIndex) {
|
||||
this.context = mContext;
|
||||
this.startIndex = startIndex;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
int count = EmojiManager.getDisplayCount() - startIndex + 1;
|
||||
count = Math.min(count, EmoticonView.EMOJI_PER_PAGE + 1);
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return startIndex + position;
|
||||
}
|
||||
|
||||
@SuppressLint({"ViewHolder", "InflateParams"})
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
convertView = LayoutInflater.from(context).inflate(R.layout.nim_emoji_item, null);
|
||||
ImageView emojiThumb = (ImageView) convertView.findViewById(R.id.imgEmoji);
|
||||
int count = EmojiManager.getDisplayCount();
|
||||
int index = startIndex + position;
|
||||
if (position == EmoticonView.EMOJI_PER_PAGE || index == count) {
|
||||
emojiThumb.setBackgroundResource(R.drawable.nim_emoji_del);
|
||||
} else if (index < count) {
|
||||
emojiThumb.setBackgroundDrawable(EmojiManager.getDisplayDrawable(context, index));
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package com.fengliyan.uikit.emoji.manager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapFactory.Options;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.LruCache;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.fengliyan.base.base.ContextHolder;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class EmojiManager {
|
||||
|
||||
private static final String EMOT_DIR = "emoji/";
|
||||
|
||||
// max cache size
|
||||
private static final int CACHE_MAX_SIZE = 1024;
|
||||
|
||||
private static Pattern pattern;
|
||||
|
||||
// default entries
|
||||
private static final List<Entry> defaultEntries = new ArrayList<Entry>();
|
||||
// text to entry
|
||||
private static final Map<String, Entry> text2entry = new HashMap<String, Entry>();
|
||||
// asset bitmap cache, key: asset path
|
||||
private static LruCache<String, Bitmap> drawableCache;
|
||||
|
||||
static {
|
||||
Context context = ContextHolder.getApplicationContext();
|
||||
|
||||
load(context, EMOT_DIR + "emoji.xml");
|
||||
|
||||
pattern = makePattern();
|
||||
|
||||
drawableCache = new LruCache<String, Bitmap>(CACHE_MAX_SIZE) {
|
||||
@Override
|
||||
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
|
||||
if (oldValue != newValue)
|
||||
oldValue.recycle();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class Entry {
|
||||
String text;
|
||||
String assetPath;
|
||||
|
||||
Entry(String text, String assetPath) {
|
||||
this.text = text;
|
||||
this.assetPath = assetPath;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// display
|
||||
//
|
||||
|
||||
public static final int getDisplayCount() {
|
||||
return defaultEntries.size();
|
||||
}
|
||||
|
||||
public static final Drawable getDisplayDrawable(Context context, int index) {
|
||||
String text = (index >= 0 && index < defaultEntries.size() ?
|
||||
defaultEntries.get(index).text : null);
|
||||
return text == null ? null : getDrawable(context, text);
|
||||
}
|
||||
|
||||
public static final String getDisplayText(int index) {
|
||||
return index >= 0 && index < defaultEntries.size() ? defaultEntries
|
||||
.get(index).text : null;
|
||||
}
|
||||
|
||||
public static final Pattern getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public static final Drawable getDrawable(Context context, String text) {
|
||||
Entry entry = text2entry.get(text);
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Bitmap cache = drawableCache.get(entry.assetPath);
|
||||
if (cache == null) {
|
||||
cache = loadAssetBitmap(context, entry.assetPath);
|
||||
}
|
||||
return new BitmapDrawable(context.getResources(), cache);
|
||||
}
|
||||
|
||||
//
|
||||
// internal
|
||||
//
|
||||
|
||||
private static Pattern makePattern() {
|
||||
return Pattern.compile(patternOfDefault());
|
||||
}
|
||||
|
||||
private static String patternOfDefault() {
|
||||
return "\\[[^\\[]{1,10}\\]";
|
||||
}
|
||||
|
||||
private static Bitmap loadAssetBitmap(Context context, String assetPath) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
Resources resources = context.getResources();
|
||||
Options options = new Options();
|
||||
options.inDensity = DisplayMetrics.DENSITY_HIGH;
|
||||
options.inScreenDensity = resources.getDisplayMetrics().densityDpi;
|
||||
options.inTargetDensity = resources.getDisplayMetrics().densityDpi;
|
||||
is = context.getAssets().open(assetPath);
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(is, new Rect(), options);
|
||||
if (bitmap != null) {
|
||||
drawableCache.put(assetPath, bitmap);
|
||||
}
|
||||
return bitmap;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final void load(Context context, String xmlPath) {
|
||||
new EntryLoader().load(context, xmlPath);
|
||||
}
|
||||
|
||||
//
|
||||
// load emoticons from asset
|
||||
//
|
||||
private static class EntryLoader extends DefaultHandler {
|
||||
private String catalog = "";
|
||||
|
||||
void load(Context context, String assetPath) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = context.getAssets().open(assetPath);
|
||||
Xml.parse(is, Xml.Encoding.UTF_8, this);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SAXException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
|
||||
if (localName.equals("Catalog")) {
|
||||
catalog = attributes.getValue(uri, "Title");
|
||||
} else if (localName.equals("Emoticon")) {
|
||||
String tag = attributes.getValue(uri, "Tag");
|
||||
String fileName = attributes.getValue(uri, "File");
|
||||
Entry entry = new Entry(tag, EMOT_DIR + catalog + "/" + fileName);
|
||||
|
||||
text2entry.put(entry.text, entry);
|
||||
if (catalog.equals("default")) {
|
||||
defaultEntries.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.fengliyan.uikit.emoji.manager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.emoji.StickerCategory;
|
||||
import com.fengliyan.uikit.emoji.StickerItem;
|
||||
import com.fengliyan.uikit.emoji.view.EmoticonView;
|
||||
|
||||
/**
|
||||
* 每屏显示的贴图
|
||||
*/
|
||||
public class StickerAdapter extends BaseAdapter {
|
||||
|
||||
private Context context;
|
||||
private StickerCategory category;
|
||||
private int startIndex;
|
||||
|
||||
public StickerAdapter(Context mContext, StickerCategory category, int startIndex) {
|
||||
this.context = mContext;
|
||||
this.category = category;
|
||||
this.startIndex = startIndex;
|
||||
}
|
||||
|
||||
public int getCount() {//获取每一页的数量
|
||||
int count = category.getStickers().size() - startIndex;
|
||||
count = Math.min(count, EmoticonView.STICKER_PER_PAGE);
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return category.getStickers().get(startIndex + position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return startIndex + position;
|
||||
}
|
||||
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
StickerViewHolder viewHolder;
|
||||
if (convertView == null) {
|
||||
convertView = View.inflate(context, R.layout.nim_sticker_picker_view, null);
|
||||
viewHolder = new StickerViewHolder();
|
||||
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.sticker_thumb_image);
|
||||
viewHolder.descLabel = (TextView) convertView.findViewById(R.id.sticker_desc_label);
|
||||
convertView.setTag(viewHolder);
|
||||
} else {
|
||||
viewHolder = (StickerViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
int index = startIndex + position;
|
||||
if (index >= category.getStickers().size()) {
|
||||
return convertView;
|
||||
}
|
||||
|
||||
StickerItem sticker = category.getStickers().get(index);
|
||||
if (sticker == null) {
|
||||
return convertView;
|
||||
}
|
||||
|
||||
Glide.with(context)
|
||||
.load(StickerManager.getInstance().getStickerUri(sticker.getCategory(), sticker.getName()))
|
||||
.apply(new RequestOptions()
|
||||
.error(R.drawable.nim_default_img_failed)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.dontAnimate())
|
||||
.into(viewHolder.imageView);
|
||||
|
||||
viewHolder.descLabel.setVisibility(View.GONE);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
class StickerViewHolder {
|
||||
public ImageView imageView;
|
||||
public TextView descLabel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.fengliyan.uikit.emoji.manager;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fengliyan.base.base.ContextHolder;
|
||||
import com.fengliyan.base.base.utils.FileUtil;
|
||||
import com.fengliyan.uikit.emoji.StickerCategory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 贴图管理类
|
||||
*/
|
||||
public class StickerManager {
|
||||
private static final String TAG = "StickerManager";
|
||||
|
||||
private static StickerManager instance;
|
||||
private static final String CATEGORY_AJMD = "ajmd";
|
||||
private static final String CATEGORY_XXY = "xxy";
|
||||
private static final String CATEGORY_LT = "lt";
|
||||
|
||||
/**
|
||||
* 数据源
|
||||
*/
|
||||
private List<StickerCategory> stickerCategories = new ArrayList<>();
|
||||
private Map<String, StickerCategory> stickerCategoryMap = new HashMap<>();
|
||||
private Map<String, Integer> stickerOrder = new HashMap<>(3);
|
||||
|
||||
public static StickerManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new StickerManager();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public StickerManager() {
|
||||
initStickerOrder();
|
||||
loadStickerCategory();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
Log.i(TAG, "Sticker Manager init...");
|
||||
}
|
||||
|
||||
private void initStickerOrder() {
|
||||
// 默认贴图顺序
|
||||
stickerOrder.put(CATEGORY_AJMD, 1);
|
||||
stickerOrder.put(CATEGORY_XXY, 2);
|
||||
stickerOrder.put(CATEGORY_LT, 3);
|
||||
}
|
||||
|
||||
private boolean isSystemSticker(String category) {
|
||||
return CATEGORY_XXY.equals(category) ||
|
||||
CATEGORY_AJMD.equals(category) ||
|
||||
CATEGORY_LT.equals(category);
|
||||
}
|
||||
|
||||
private int getStickerOrder(String categoryName) {
|
||||
if (stickerOrder.containsKey(categoryName)) {
|
||||
return stickerOrder.get(categoryName);
|
||||
} else {
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadStickerCategory() {
|
||||
AssetManager assetManager = ContextHolder.getApplicationContext().getResources().getAssets();
|
||||
try {
|
||||
String[] files = assetManager.list("sticker");
|
||||
StickerCategory category;
|
||||
for (String name : files) {
|
||||
if (!FileUtil.hasExtentsion(name)) {
|
||||
category = new StickerCategory(name, name, true, getStickerOrder(name));
|
||||
stickerCategories.add(category);
|
||||
stickerCategoryMap.put(name, category);
|
||||
}
|
||||
}
|
||||
// 排序
|
||||
Collections.sort(stickerCategories, new Comparator<StickerCategory>() {
|
||||
@Override
|
||||
public int compare(StickerCategory l, StickerCategory r) {
|
||||
return l.getOrder() - r.getOrder();
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized List<StickerCategory> getCategories() {
|
||||
return stickerCategories;
|
||||
}
|
||||
|
||||
public synchronized StickerCategory getCategory(String name) {
|
||||
return stickerCategoryMap.get(name);
|
||||
}
|
||||
|
||||
public String getStickerUri(String categoryName, String stickerName) {
|
||||
StickerManager manager = StickerManager.getInstance();
|
||||
StickerCategory category = manager.getCategory(categoryName);
|
||||
if (category == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isSystemSticker(categoryName)) {
|
||||
if (!stickerName.contains(".png")) {
|
||||
stickerName += ".png";
|
||||
}
|
||||
|
||||
String path = "sticker/" + category.getName() + "/" + stickerName;
|
||||
return "file:///android_asset/" + path;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
package com.fengliyan.uikit.emoji.utils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.ThumbnailUtils;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import com.fengliyan.uikit.emoji.AttachmentStore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class BitmapDecoder {
|
||||
public static Bitmap decode(InputStream is) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
|
||||
// RGB_565
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565;
|
||||
|
||||
/**
|
||||
* 在4.4上,如果之前is标记被移动过,会导致解码失败
|
||||
*/
|
||||
try {
|
||||
if (is.markSupported()) {
|
||||
is.reset();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
return BitmapFactory.decodeStream(is, null, options);
|
||||
} catch (OutOfMemoryError e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Bitmap decodeSampledForDisplay(String pathName) {
|
||||
return decodeSampledForDisplay(pathName, true);
|
||||
}
|
||||
|
||||
public static Bitmap decodeSampledForDisplay(String pathName, boolean withTextureLimit) {
|
||||
float ratio = 5f;
|
||||
int[][] reqBounds = new int[][]{
|
||||
new int[]{ScreenUtil.screenWidth * 2, ScreenUtil.screenHeight},
|
||||
new int[]{ScreenUtil.screenWidth, ScreenUtil.screenHeight * 2},
|
||||
new int[]{(int) (ScreenUtil.screenWidth * 1.414), (int) (ScreenUtil.screenHeight * 1.414)},
|
||||
};
|
||||
|
||||
// decode bound
|
||||
int[] bound = decodeBound(pathName);
|
||||
// pick request bound
|
||||
int[] reqBound = pickReqBoundWithRatio(bound, reqBounds, ratio);
|
||||
|
||||
int width = bound[0];
|
||||
int height = bound[1];
|
||||
int reqWidth = reqBound[0];
|
||||
int reqHeight = reqBound[1];
|
||||
|
||||
// calculate sample size
|
||||
int sampleSize = SampleSizeUtil.calculateSampleSize(width, height, reqWidth, reqHeight);
|
||||
|
||||
if (withTextureLimit) {
|
||||
// adjust sample size
|
||||
sampleSize = SampleSizeUtil.adjustSampleSizeWithTexture(sampleSize, width, height);
|
||||
}
|
||||
|
||||
int RETRY_LIMIT = 5;
|
||||
Bitmap bitmap = decodeSampled(pathName, sampleSize);
|
||||
while (bitmap == null && RETRY_LIMIT > 0) {
|
||||
sampleSize++;
|
||||
RETRY_LIMIT--;
|
||||
bitmap = decodeSampled(pathName, sampleSize);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static int[] decodeBound(String pathName) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(pathName, options);
|
||||
|
||||
return new int[]{options.outWidth, options.outHeight};
|
||||
}
|
||||
|
||||
public static int[] decodeBound(Resources res, int resId) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeResource(res, resId, options);
|
||||
|
||||
return new int[]{options.outWidth, options.outHeight};
|
||||
}
|
||||
|
||||
private static int[] pickReqBoundWithRatio(int[] bound, int[][] reqBounds, float ratio) {
|
||||
float hRatio = bound[1] == 0 ? 0 : (float) bound[0] / (float) bound[1];
|
||||
float vRatio = bound[0] == 0 ? 0 : (float) bound[1] / (float) bound[0];
|
||||
|
||||
if (hRatio >= ratio) {
|
||||
return reqBounds[0];
|
||||
} else if (vRatio >= ratio) {
|
||||
return reqBounds[1];
|
||||
} else {
|
||||
return reqBounds[2];
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap decodeSampled(String pathName, int sampleSize) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
|
||||
// RGB_565
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565;
|
||||
// sample size
|
||||
options.inSampleSize = sampleSize;
|
||||
|
||||
Bitmap bitmap = null;
|
||||
try {
|
||||
bitmap = BitmapFactory.decodeFile(pathName, options);
|
||||
} catch (OutOfMemoryError e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
return checkInBitmap(bitmap, options, pathName);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
private static Bitmap checkInBitmap(Bitmap bitmap,
|
||||
BitmapFactory.Options options, String path) {
|
||||
boolean honeycomb = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
|
||||
if (honeycomb && bitmap != options.inBitmap && options.inBitmap != null) {
|
||||
options.inBitmap.recycle();
|
||||
options.inBitmap = null;
|
||||
}
|
||||
|
||||
if (bitmap == null) {
|
||||
try {
|
||||
bitmap = BitmapFactory.decodeFile(path, options);
|
||||
} catch (OutOfMemoryError e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static int[] decodeBound(File file) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(file);
|
||||
int[] bound = decodeBound(is);
|
||||
return bound;
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new int[]{0, 0};
|
||||
}
|
||||
|
||||
public static int[] decodeBound(InputStream is) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
|
||||
return new int[]{options.outWidth, options.outHeight};
|
||||
}
|
||||
|
||||
public static Bitmap decodeSampled(InputStream is, int reqWidth, int reqHeight) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
|
||||
// RGB_565
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565;
|
||||
// sample size
|
||||
options.inSampleSize = getSampleSize(is, reqWidth, reqHeight);
|
||||
|
||||
try {
|
||||
return BitmapFactory.decodeStream(is, null, options);
|
||||
} catch (OutOfMemoryError e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Bitmap decodeSampled(String pathName, int reqWidth, int reqHeight) {
|
||||
return decodeSampled(pathName, getSampleSize(pathName, reqWidth, reqHeight));
|
||||
}
|
||||
|
||||
public static int getSampleSize(InputStream is, int reqWidth, int reqHeight) {
|
||||
// decode bound
|
||||
int[] bound = decodeBound(is);
|
||||
|
||||
// calculate sample size
|
||||
int sampleSize = SampleSizeUtil.calculateSampleSize(bound[0], bound[1], reqWidth, reqHeight);
|
||||
|
||||
return sampleSize;
|
||||
}
|
||||
|
||||
public static int getSampleSize(String pathName, int reqWidth, int reqHeight) {
|
||||
// decode bound
|
||||
int[] bound = decodeBound(pathName);
|
||||
|
||||
// calculate sample size
|
||||
int sampleSize = SampleSizeUtil.calculateSampleSize(bound[0], bound[1], reqWidth, reqHeight);
|
||||
|
||||
return sampleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* ******************************* decode resource ******************************************
|
||||
*/
|
||||
|
||||
public static Bitmap decodeSampled(Resources resources, int resId, int reqWidth, int reqHeight) {
|
||||
return decodeSampled(resources, resId, getSampleSize(resources, resId, reqWidth, reqHeight));
|
||||
}
|
||||
|
||||
public static int getSampleSize(Resources resources, int resId, int reqWidth, int reqHeight) {
|
||||
// decode bound
|
||||
int[] bound = decodeBound(resources, resId);
|
||||
|
||||
// calculate sample size
|
||||
int sampleSize = SampleSizeUtil.calculateSampleSize(bound[0], bound[1], reqWidth, reqHeight);
|
||||
|
||||
return sampleSize;
|
||||
}
|
||||
|
||||
|
||||
public static Bitmap decodeSampled(Resources res, int resId, int sampleSize) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
|
||||
// RGB_565
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565;
|
||||
// sample size
|
||||
options.inSampleSize = sampleSize;
|
||||
|
||||
try {
|
||||
return BitmapFactory.decodeResource(res, resId, options);
|
||||
} catch (OutOfMemoryError e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean extractThumbnail(String videoPath, String thumbPath) {
|
||||
if (!AttachmentStore.isFileExist(thumbPath)) {
|
||||
Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail(videoPath, MediaStore.Images.Thumbnails.MINI_KIND);
|
||||
if (thumbnail != null) {
|
||||
AttachmentStore.saveBitmap(thumbnail, thumbPath, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package com.fengliyan.uikit.emoji.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.text.Editable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.fengliyan.uikit.emoji.manager.EmojiManager;
|
||||
import com.fengliyan.uikit.emoji.ImageSpanAlignCenter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MoonUtil {
|
||||
private static final float DEF_SCALE = 0.6f;
|
||||
private static final float SMALL_SCALE = 0.45F;
|
||||
|
||||
public static void identifyFaceExpression(Context context,
|
||||
View textView, String value, int align) {
|
||||
identifyFaceExpression(context, textView, value, align, DEF_SCALE);
|
||||
}
|
||||
|
||||
public static void identifyFaceExpressionAndATags(Context context,
|
||||
View textView, String value, int align) {
|
||||
SpannableString mSpannableString = makeSpannableStringTags(context, value, DEF_SCALE, align);
|
||||
viewSetText(textView, mSpannableString);
|
||||
}
|
||||
|
||||
/**
|
||||
* 具体类型的view设置内容
|
||||
*
|
||||
* @param textView
|
||||
* @param mSpannableString
|
||||
*/
|
||||
private static void viewSetText(View textView, SpannableString mSpannableString) {
|
||||
if (textView instanceof TextView) {
|
||||
TextView tv = (TextView) textView;
|
||||
tv.setText(mSpannableString);
|
||||
} else if (textView instanceof EditText) {
|
||||
EditText et = (EditText) textView;
|
||||
et.setText(mSpannableString);
|
||||
}
|
||||
}
|
||||
|
||||
public static void identifyFaceExpression(Context context,
|
||||
View textView, String value, int align, float scale) {
|
||||
SpannableString mSpannableString = replaceEmoticons(context, value, scale, align);
|
||||
viewSetText(textView, mSpannableString);
|
||||
}
|
||||
|
||||
/**
|
||||
* lstmsgviewholder类使用,只需显示a标签对应的文本
|
||||
*/
|
||||
public static void identifyFaceExpressionAndTags(Context context,
|
||||
View textView, String value, int align, float scale) {
|
||||
SpannableString mSpannableString = makeSpannableStringTags(context, value, scale, align, false);
|
||||
viewSetText(textView, mSpannableString);
|
||||
}
|
||||
|
||||
private static SpannableString replaceEmoticons(Context context, String value, float scale, int align) {
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
value = "";
|
||||
}
|
||||
|
||||
SpannableString mSpannableString = new SpannableString(value);
|
||||
Matcher matcher = EmojiManager.getPattern().matcher(value);
|
||||
while (matcher.find()) {
|
||||
int start = matcher.start();
|
||||
int end = matcher.end();
|
||||
String emot = value.substring(start, end);
|
||||
Drawable d = getEmotDrawable(context, emot, scale);
|
||||
if (d != null) {
|
||||
ImageSpan span = new ImageSpan(d, align);
|
||||
mSpannableString.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
return mSpannableString;
|
||||
}
|
||||
|
||||
private static Pattern mATagPattern = Pattern.compile("<a.*?>.*?</a>");
|
||||
|
||||
public static SpannableString makeSpannableStringTags(Context context, String value, float scale, int align) {
|
||||
return makeSpannableStringTags(context, value, DEF_SCALE, align, true);
|
||||
}
|
||||
|
||||
public static SpannableString makeSpannableStringTags(Context context, String value, float scale, int align, boolean bTagClickable) {
|
||||
ArrayList<ATagSpan> tagSpans = new ArrayList<ATagSpan>();
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
value = "";
|
||||
}
|
||||
//a标签需要替换原始文本,放在moonutil类中
|
||||
Matcher aTagMatcher = mATagPattern.matcher(value);
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
while (aTagMatcher.find()) {
|
||||
start = aTagMatcher.start();
|
||||
end = aTagMatcher.end();
|
||||
String atagString = value.substring(start, end);
|
||||
ATagSpan tagSpan = getTagSpan(atagString);
|
||||
value = value.substring(0, start) + tagSpan.getTag() + value.substring(end);
|
||||
tagSpan.setRange(start, start + tagSpan.getTag().length());
|
||||
tagSpans.add(tagSpan);
|
||||
aTagMatcher = mATagPattern.matcher(value);
|
||||
}
|
||||
|
||||
|
||||
SpannableString mSpannableString = new SpannableString(value);
|
||||
Matcher matcher = EmojiManager.getPattern().matcher(value);
|
||||
while (matcher.find()) {
|
||||
start = matcher.start();
|
||||
end = matcher.end();
|
||||
String emot = value.substring(start, end);
|
||||
Drawable d = getEmotDrawable(context, emot, scale);
|
||||
if (d != null) {
|
||||
ImageSpan span = align == -1 ? new ImageSpanAlignCenter(d) : new ImageSpan(d, align);
|
||||
mSpannableString.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
|
||||
if (bTagClickable) {
|
||||
for (ATagSpan tagSpan : tagSpans) {
|
||||
mSpannableString.setSpan(tagSpan, tagSpan.start, tagSpan.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
|
||||
return mSpannableString;
|
||||
}
|
||||
|
||||
public static void replaceEmoticons(Context context, Editable editable, int start, int count) {
|
||||
if (count <= 0 || editable.length() < start + count)
|
||||
return;
|
||||
|
||||
CharSequence s = editable.subSequence(start, start + count);
|
||||
Matcher matcher = EmojiManager.getPattern().matcher(s);
|
||||
while (matcher.find()) {
|
||||
int from = start + matcher.start();
|
||||
int to = start + matcher.end();
|
||||
String emot = editable.subSequence(from, to).toString();
|
||||
Drawable d = getEmotDrawable(context, emot, SMALL_SCALE);
|
||||
if (d != null) {
|
||||
ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BOTTOM);
|
||||
editable.setSpan(span, from, to, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Drawable getEmotDrawable(Context context, String text, float scale) {
|
||||
Drawable drawable = EmojiManager.getDrawable(context, text);
|
||||
|
||||
// scale
|
||||
if (drawable != null) {
|
||||
int width = (int) (drawable.getIntrinsicWidth() * scale);
|
||||
int height = (int) (drawable.getIntrinsicHeight() * scale);
|
||||
drawable.setBounds(0, 0, width, height);
|
||||
}
|
||||
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private static ATagSpan getTagSpan(String text) {
|
||||
String href = null;
|
||||
String tag = null;
|
||||
if (text.toLowerCase().contains("href")) {
|
||||
int start = text.indexOf("\"");
|
||||
int end = text.indexOf("\"", start + 1);
|
||||
if (end > start)
|
||||
href = text.substring(start + 1, end);
|
||||
}
|
||||
int start = text.indexOf(">");
|
||||
int end = text.indexOf("<", start);
|
||||
if (end > start)
|
||||
tag = text.substring(start + 1, end);
|
||||
return new ATagSpan(tag, href);
|
||||
|
||||
}
|
||||
|
||||
private static class ATagSpan extends ClickableSpan {
|
||||
private int start;
|
||||
private int end;
|
||||
private String mUrl;
|
||||
private String tag;
|
||||
|
||||
ATagSpan(String tag, String url) {
|
||||
this.tag = tag;
|
||||
this.mUrl = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDrawState(TextPaint ds) {
|
||||
super.updateDrawState(ds);
|
||||
ds.setUnderlineText(true);
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void setRange(int start, int end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
try {
|
||||
if (TextUtils.isEmpty(mUrl))
|
||||
return;
|
||||
Uri uri = Uri.parse(mUrl);
|
||||
String scheme = uri.getScheme();
|
||||
if (TextUtils.isEmpty(scheme)) {
|
||||
mUrl = "http://" + mUrl;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.fengliyan.uikit.emoji.utils;
|
||||
|
||||
import android.opengl.GLES10;
|
||||
|
||||
public class SampleSizeUtil {
|
||||
|
||||
public static int calculateSampleSize(String imagePath, int totalPixel) {
|
||||
int[] bound = BitmapDecoder.decodeBound(imagePath);
|
||||
return calculateSampleSize(bound[0], bound[1], totalPixel);
|
||||
}
|
||||
|
||||
public static int calculateSampleSize(int width, int height, int totalPixel) {
|
||||
int ratio = 1;
|
||||
|
||||
if (width > 0 && height > 0) {
|
||||
ratio = (int) Math.sqrt((float) (width * height) / totalPixel);
|
||||
if (ratio < 1) {
|
||||
ratio = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ratio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate an inSampleSize for use in a {@link android.graphics.BitmapFactory.Options}
|
||||
* object when decoding bitmaps using the decode* methods from
|
||||
* {@link android.graphics.BitmapFactory}. This implementation calculates the closest
|
||||
* inSampleSize that will result in the final decoded bitmap having a width
|
||||
* and height equal to or larger than the requested width and height. This
|
||||
* implementation does not ensure a power of 2 is returned for inSampleSize
|
||||
* which can be faster when decoding but results in a larger bitmap which
|
||||
* isn't as useful for caching purposes.
|
||||
*
|
||||
* @param width
|
||||
* @param height
|
||||
* @param reqWidth
|
||||
* @param reqHeight
|
||||
* @return
|
||||
*/
|
||||
public static int calculateSampleSize(int width, int height, int reqWidth, int reqHeight) {
|
||||
// can't proceed
|
||||
if (width <= 0 || height <= 0) {
|
||||
return 1;
|
||||
}
|
||||
// can't proceed
|
||||
if (reqWidth <= 0 && reqHeight <= 0) {
|
||||
return 1;
|
||||
} else if (reqWidth <= 0) {
|
||||
reqWidth = (int) (width * reqHeight / (float) height + 0.5f);
|
||||
} else if (reqHeight <= 0) {
|
||||
reqHeight = (int) (height * reqWidth / (float) width + 0.5f);
|
||||
}
|
||||
|
||||
int inSampleSize = 1;
|
||||
|
||||
if (height > reqHeight || width > reqWidth) {
|
||||
// Calculate ratios of height and width to requested height and width
|
||||
final int heightRatio = Math.round((float) height / (float) reqHeight);
|
||||
final int widthRatio = Math.round((float) width / (float) reqWidth);
|
||||
|
||||
// Choose the smallest ratio as inSampleSize value, this will
|
||||
// guarantee a final image
|
||||
// with both dimensions larger than or equal to the requested height and width.
|
||||
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
|
||||
if (inSampleSize == 0) {
|
||||
inSampleSize = 1;
|
||||
}
|
||||
|
||||
// This offers some additional logic in case the image has a strange
|
||||
// aspect ratio. For example, a panorama may have a much larger
|
||||
// width than height. In these cases the total pixels might still
|
||||
// end up being too large to fit comfortably in memory, so we should
|
||||
// be more aggressive with sample down the image (=larger
|
||||
// inSampleSize).
|
||||
|
||||
final float totalPixels = width * height;
|
||||
|
||||
// Anything more than 2x the requested pixels we'll sample down
|
||||
// further
|
||||
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
|
||||
|
||||
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
|
||||
inSampleSize++;
|
||||
}
|
||||
}
|
||||
|
||||
return inSampleSize;
|
||||
}
|
||||
|
||||
public static final int adjustSampleSizeWithTexture(int sampleSize, int width, int height) {
|
||||
int textureSize = getTextureSize();
|
||||
|
||||
if ((textureSize > 0) && ((width > sampleSize) || (height > sampleSize))) {
|
||||
while ((width / (float) sampleSize) > textureSize || (height / (float) sampleSize) > textureSize) {
|
||||
sampleSize++;
|
||||
}
|
||||
|
||||
// 2的指数对齐
|
||||
sampleSize = SampleSizeUtil.roundup2n(sampleSize);
|
||||
}
|
||||
|
||||
return sampleSize;
|
||||
}
|
||||
|
||||
private static int textureSize = 0;
|
||||
|
||||
//存在第二次拿拿不到的情况,所以把拿到的数据用一个static变量保存下来
|
||||
public static final int getTextureSize() {
|
||||
if (textureSize > 0) {
|
||||
return textureSize;
|
||||
}
|
||||
|
||||
int[] params = new int[1];
|
||||
GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, params, 0);
|
||||
textureSize = params[0];
|
||||
|
||||
return textureSize;
|
||||
}
|
||||
|
||||
// 将x向上对齐到2的幂指数
|
||||
private static final int roundup2n(int x) {
|
||||
if ((x & (x - 1)) == 0) {
|
||||
return x;
|
||||
}
|
||||
int pos = 0;
|
||||
while (x > 0) {
|
||||
x >>= 1;
|
||||
++pos;
|
||||
}
|
||||
return 1 << pos;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.fengliyan.uikit.emoji.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
import com.fengliyan.base.base.ContextHolder;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class ScreenUtil {
|
||||
private static final String TAG = "Demo.ScreenUtil";
|
||||
|
||||
private static double RATIO = 0.85;
|
||||
|
||||
public static int screenWidth;
|
||||
public static int screenHeight;
|
||||
public static int screenMin;// 宽高中,小的一边
|
||||
public static int screenMax;// 宽高中,较大的值
|
||||
|
||||
public static float density;
|
||||
public static float scaleDensity;
|
||||
public static float xdpi;
|
||||
public static float ydpi;
|
||||
public static int densityDpi;
|
||||
|
||||
public static int dialogWidth;
|
||||
public static int statusbarheight;
|
||||
public static int navbarheight;
|
||||
|
||||
static {
|
||||
init(ContextHolder.getApplicationContext());
|
||||
}
|
||||
|
||||
public static int dip2px(float dipValue) {
|
||||
return (int) (dipValue * density + 0.5f);
|
||||
}
|
||||
|
||||
public static int px2dip(float pxValue) {
|
||||
return (int) (pxValue / density + 0.5f);
|
||||
}
|
||||
|
||||
public static int sp2px(float spValue) {
|
||||
return (int) (spValue * scaleDensity + 0.5f);
|
||||
}
|
||||
|
||||
public static int getDialogWidth() {
|
||||
dialogWidth = (int) (screenMin * RATIO);
|
||||
return dialogWidth;
|
||||
}
|
||||
|
||||
public static void init(Context context) {
|
||||
if (null == context) {
|
||||
return;
|
||||
}
|
||||
DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();
|
||||
screenWidth = dm.widthPixels;
|
||||
screenHeight = dm.heightPixels;
|
||||
screenMin = (screenWidth > screenHeight) ? screenHeight : screenWidth;
|
||||
density = dm.density;
|
||||
scaleDensity = dm.scaledDensity;
|
||||
xdpi = dm.xdpi;
|
||||
ydpi = dm.ydpi;
|
||||
densityDpi = dm.densityDpi;
|
||||
|
||||
Log.d(TAG, "screenWidth=" + screenWidth + " screenHeight=" + screenHeight + " density=" + density);
|
||||
}
|
||||
|
||||
public static int getDisplayWidth() {
|
||||
if (screenWidth == 0) {
|
||||
GetInfo(ContextHolder.getApplicationContext());
|
||||
}
|
||||
return screenWidth;
|
||||
}
|
||||
|
||||
public static int getDisplayHeight() {
|
||||
if (screenHeight == 0) {
|
||||
GetInfo(ContextHolder.getApplicationContext());
|
||||
}
|
||||
return screenHeight;
|
||||
}
|
||||
|
||||
public static void GetInfo(Context context) {
|
||||
if (null == context) {
|
||||
return;
|
||||
}
|
||||
DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();
|
||||
screenWidth = dm.widthPixels;
|
||||
screenHeight = dm.heightPixels;
|
||||
screenMin = (screenWidth > screenHeight) ? screenHeight : screenWidth;
|
||||
screenMax = (screenWidth < screenHeight) ? screenHeight : screenWidth;
|
||||
density = dm.density;
|
||||
scaleDensity = dm.scaledDensity;
|
||||
xdpi = dm.xdpi;
|
||||
ydpi = dm.ydpi;
|
||||
densityDpi = dm.densityDpi;
|
||||
statusbarheight = getStatusBarHeight(context);
|
||||
navbarheight = getNavBarHeight(context);
|
||||
Log.d(TAG, "screenWidth=" + screenWidth + " screenHeight=" + screenHeight + " density=" + density);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
if (statusbarheight == 0) {
|
||||
statusbarheight = ScreenUtil.dip2px(25);
|
||||
}
|
||||
return statusbarheight;
|
||||
}
|
||||
|
||||
public static int getNavBarHeight(Context context) {
|
||||
Resources resources = context.getResources();
|
||||
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
return resources.getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.fengliyan.uikit.emoji.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
/**
|
||||
* 选中图片控件
|
||||
*/
|
||||
public class CheckedImageButton extends ImageButton {
|
||||
|
||||
private boolean checked;
|
||||
|
||||
private int normalBkResId;
|
||||
|
||||
private int checkedBkResId;
|
||||
|
||||
private Drawable normalImage;
|
||||
|
||||
private Drawable checkedImage;
|
||||
|
||||
private int leftPadding, topPadding, rightPadding, bottomPadding;
|
||||
|
||||
public CheckedImageButton(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public CheckedImageButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public CheckedImageButton(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
public void setPaddingValue(int value) {
|
||||
setPaddingValue(value, value, value, value);
|
||||
}
|
||||
|
||||
public void setPaddingValue(int left, int top, int right, int bottom) {
|
||||
leftPadding = left;
|
||||
topPadding = top;
|
||||
rightPadding = right;
|
||||
bottomPadding = bottom;
|
||||
setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
|
||||
}
|
||||
|
||||
public boolean isChecked() {
|
||||
return checked;
|
||||
}
|
||||
|
||||
public void setChecked(boolean push) {
|
||||
this.checked = push;
|
||||
|
||||
Drawable image = push ? checkedImage : normalImage;
|
||||
if (image != null) {
|
||||
updateImage(image);
|
||||
}
|
||||
|
||||
int background = push ? checkedBkResId : normalBkResId;
|
||||
if (background != 0) {
|
||||
updateBackground(background);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNormalBkResId(int normalBkResId) {
|
||||
this.normalBkResId = normalBkResId;
|
||||
updateBackground(normalBkResId);
|
||||
}
|
||||
|
||||
public void setCheckedBkResId(int checkedBkResId) {
|
||||
this.checkedBkResId = checkedBkResId;
|
||||
}
|
||||
|
||||
public void setNormalImageId(int normalResId) {
|
||||
normalImage = getResources().getDrawable(normalResId);
|
||||
updateImage(normalImage);
|
||||
}
|
||||
|
||||
public void setCheckedImageId(int pushedResId) {
|
||||
checkedImage = getResources().getDrawable(pushedResId);
|
||||
}
|
||||
|
||||
public void setNormalImage(Bitmap bitmap) {
|
||||
|
||||
this.normalImage = new BitmapDrawable(getResources(), bitmap);
|
||||
updateImage(this.normalImage);
|
||||
}
|
||||
|
||||
public void setCheckedImage(Bitmap bitmap) {
|
||||
this.checkedImage = new BitmapDrawable(getResources(), bitmap);
|
||||
}
|
||||
|
||||
private void updateBackground(int resId) {
|
||||
setBackgroundResource(resId);
|
||||
setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
|
||||
// int padding = ScreenUtil.dip2px(7);
|
||||
// setPadding(padding, padding, padding, padding);
|
||||
}
|
||||
|
||||
private void updateImage(Drawable drawable) {
|
||||
// setScaleType(ScaleType.FIT_CENTER);
|
||||
setImageDrawable(drawable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
package com.fengliyan.uikit.emoji.view;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.emoji.IEmoticonCategoryChanged;
|
||||
import com.fengliyan.uikit.emoji.IEmoticonSelectedListener;
|
||||
import com.fengliyan.uikit.emoji.StickerCategory;
|
||||
import com.fengliyan.uikit.emoji.manager.StickerManager;
|
||||
import com.fengliyan.uikit.emoji.utils.BitmapDecoder;
|
||||
import com.fengliyan.uikit.emoji.utils.ScreenUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 贴图表情选择控件
|
||||
*/
|
||||
public class EmoticonPickerView extends LinearLayout implements IEmoticonCategoryChanged {
|
||||
|
||||
private Context context;
|
||||
|
||||
private IEmoticonSelectedListener listener;
|
||||
|
||||
private boolean loaded = false;
|
||||
|
||||
private boolean withSticker;
|
||||
|
||||
private EmoticonView gifView;
|
||||
|
||||
private ViewPager currentEmojiPage;
|
||||
|
||||
private LinearLayout pageNumberLayout;//页面布局
|
||||
|
||||
private HorizontalScrollView scrollView;
|
||||
|
||||
private LinearLayout tabView;
|
||||
|
||||
private int categoryIndex;
|
||||
|
||||
private Handler uiHandler;
|
||||
|
||||
public EmoticonPickerView(Context context) {
|
||||
super(context);
|
||||
|
||||
init(context);
|
||||
}
|
||||
|
||||
public EmoticonPickerView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
init(context);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public EmoticonPickerView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
this.context = context;
|
||||
this.uiHandler = new Handler(context.getMainLooper());
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.nim_emoji_layout, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
setupEmojView();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
public void show(IEmoticonSelectedListener listener) {
|
||||
setListener(listener);
|
||||
|
||||
if (loaded)
|
||||
return;
|
||||
loadStickers();
|
||||
loaded = true;
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
public void setListener(IEmoticonSelectedListener listener) {
|
||||
if (listener != null) {
|
||||
this.listener = listener;
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected void setupEmojView() {
|
||||
currentEmojiPage = (ViewPager) findViewById(R.id.scrPlugin);
|
||||
pageNumberLayout = (LinearLayout) findViewById(R.id.layout_scr_bottom);
|
||||
tabView = (LinearLayout) findViewById(R.id.emoj_tab_view);
|
||||
scrollView = (HorizontalScrollView) findViewById(R.id.emoj_tab_view_container);
|
||||
|
||||
findViewById(R.id.top_divider_line).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// 添加各个tab按钮
|
||||
OnClickListener tabCheckListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onEmoticonBtnChecked(v.getId());
|
||||
}
|
||||
};
|
||||
|
||||
private void loadStickers() {
|
||||
if (!withSticker) {
|
||||
scrollView.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
final StickerManager manager = StickerManager.getInstance();
|
||||
|
||||
tabView.removeAllViews();
|
||||
|
||||
int index = 0;
|
||||
|
||||
// emoji表情
|
||||
CheckedImageButton btn = addEmoticonTabBtn(index++, tabCheckListener);
|
||||
btn.setNormalImageId(R.drawable.nim_emoji_icon_inactive);
|
||||
btn.setCheckedImageId(R.drawable.nim_emoji_icon);
|
||||
|
||||
// 贴图
|
||||
List<StickerCategory> categories = manager.getCategories();
|
||||
for (StickerCategory category : categories) {
|
||||
btn = addEmoticonTabBtn(index++, tabCheckListener);
|
||||
setCheckedButtomImage(btn, category);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private CheckedImageButton addEmoticonTabBtn(int index, OnClickListener listener) {
|
||||
CheckedImageButton emotBtn = new CheckedImageButton(context);
|
||||
emotBtn.setNormalBkResId(R.drawable.nim_sticker_button_background_normal_layer_list);
|
||||
emotBtn.setCheckedBkResId(R.drawable.nim_sticker_button_background_pressed_layer_list);
|
||||
emotBtn.setId(index);
|
||||
emotBtn.setOnClickListener(listener);
|
||||
emotBtn.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
emotBtn.setPaddingValue(ScreenUtil.dip2px(7));
|
||||
|
||||
final int emojiBtnWidth = ScreenUtil.dip2px(50);
|
||||
final int emojiBtnHeight = ScreenUtil.dip2px(44);
|
||||
|
||||
tabView.addView(emotBtn);
|
||||
|
||||
ViewGroup.LayoutParams emojBtnLayoutParams = emotBtn.getLayoutParams();
|
||||
emojBtnLayoutParams.width = emojiBtnWidth;
|
||||
emojBtnLayoutParams.height = emojiBtnHeight;
|
||||
emotBtn.setLayoutParams(emojBtnLayoutParams);
|
||||
|
||||
return emotBtn;
|
||||
}
|
||||
|
||||
private void setCheckedButtomImage(CheckedImageButton btn, StickerCategory category) {
|
||||
try {
|
||||
InputStream is = category.getCoverNormalInputStream(context);
|
||||
if (is != null) {
|
||||
Bitmap bmp = BitmapDecoder.decode(is);
|
||||
btn.setNormalImage(bmp);
|
||||
is.close();
|
||||
}
|
||||
is = category.getCoverPressedInputStream(context);
|
||||
if (is != null) {
|
||||
Bitmap bmp = BitmapDecoder.decode(is);
|
||||
btn.setCheckedImage(bmp);
|
||||
is.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void onEmoticonBtnChecked(int index) {
|
||||
updateTabButton(index);
|
||||
showEmotPager(index);
|
||||
}
|
||||
|
||||
private void updateTabButton(int index) {
|
||||
for (int i = 0; i < tabView.getChildCount(); ++i) {
|
||||
View child = tabView.getChildAt(i);
|
||||
if (child instanceof FrameLayout) {
|
||||
child = ((FrameLayout) child).getChildAt(0);
|
||||
}
|
||||
|
||||
if (child != null && child instanceof CheckedImageButton) {
|
||||
CheckedImageButton tabButton = (CheckedImageButton) child;
|
||||
if (tabButton.isChecked() && i != index) {
|
||||
tabButton.setChecked(false);
|
||||
} else if (!tabButton.isChecked() && i == index) {
|
||||
tabButton.setChecked(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showEmotPager(int index) {
|
||||
if (gifView == null) {
|
||||
gifView = new EmoticonView(context, listener, currentEmojiPage, pageNumberLayout);
|
||||
gifView.setCategoryChangCheckedCallback(this);
|
||||
}
|
||||
|
||||
gifView.showStickers(index);
|
||||
}
|
||||
|
||||
private void showEmojiView() {
|
||||
if (gifView == null) {
|
||||
gifView = new EmoticonView(context, listener, currentEmojiPage, pageNumberLayout);
|
||||
}
|
||||
gifView.showEmojis();
|
||||
}
|
||||
|
||||
private void show() {
|
||||
if (listener == null) {
|
||||
|
||||
}
|
||||
if (!withSticker) {
|
||||
showEmojiView();
|
||||
} else {
|
||||
onEmoticonBtnChecked(0);
|
||||
setSelectedVisible(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setSelectedVisible(final int index) {
|
||||
final Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (scrollView.getChildAt(0).getWidth() == 0) {
|
||||
uiHandler.postDelayed(this, 100);
|
||||
}
|
||||
int x = -1;
|
||||
View child = tabView.getChildAt(index);
|
||||
if (child != null) {
|
||||
if (child.getRight() > scrollView.getWidth()) {
|
||||
x = child.getRight() - scrollView.getWidth();
|
||||
}
|
||||
}
|
||||
if (x != -1) {
|
||||
scrollView.smoothScrollTo(x, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
uiHandler.postDelayed(runnable, 100);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCategoryChanged(int index) {
|
||||
if (categoryIndex == index) {
|
||||
return;
|
||||
}
|
||||
|
||||
categoryIndex = index;
|
||||
updateTabButton(index);
|
||||
}
|
||||
|
||||
public void setWithSticker(boolean withSticker) {
|
||||
this.withSticker = withSticker;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
package com.fengliyan.uikit.emoji.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.emoji.IEmoticonCategoryChanged;
|
||||
import com.fengliyan.uikit.emoji.IEmoticonSelectedListener;
|
||||
import com.fengliyan.uikit.emoji.StickerCategory;
|
||||
import com.fengliyan.uikit.emoji.StickerItem;
|
||||
import com.fengliyan.uikit.emoji.manager.EmojiAdapter;
|
||||
import com.fengliyan.uikit.emoji.manager.EmojiManager;
|
||||
import com.fengliyan.uikit.emoji.manager.StickerAdapter;
|
||||
import com.fengliyan.uikit.emoji.manager.StickerManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 贴图显示viewpager
|
||||
*/
|
||||
public class EmoticonView {
|
||||
|
||||
private ViewPager emotPager;
|
||||
private LinearLayout pageNumberLayout;
|
||||
/**
|
||||
* 总页数.
|
||||
*/
|
||||
private int pageCount;
|
||||
|
||||
/**
|
||||
* 每页显示的数量,Adapter保持一致.
|
||||
*/
|
||||
public static final int EMOJI_PER_PAGE = 27; // 最后一个是删除键
|
||||
public static final int STICKER_PER_PAGE = 8;
|
||||
|
||||
private Context context;
|
||||
private IEmoticonSelectedListener listener;
|
||||
private EmoticonViewPaperAdapter pagerAdapter = new EmoticonViewPaperAdapter();
|
||||
|
||||
/**
|
||||
* 所有表情贴图支持横向滑动切换
|
||||
*/
|
||||
private int categoryIndex; // 当套贴图的在picker中的索引
|
||||
private boolean isDataInitialized = false; // 数据源只需要初始化一次,变更时再初始化
|
||||
private List<StickerCategory> categoryDataList; // 表情贴图数据源
|
||||
private List<Integer> categoryPageNumberList; // 每套表情贴图对应的页数
|
||||
private int[] pagerIndexInfo = new int[2]; // 0:category index;1:pager index in category
|
||||
private IEmoticonCategoryChanged categoryChangedCallback; // 横向滑动切换时回调picker
|
||||
|
||||
public EmoticonView(Context context, IEmoticonSelectedListener mlistener,
|
||||
ViewPager mCurPage, LinearLayout pageNumberLayout) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.listener = mlistener;
|
||||
this.pageNumberLayout = pageNumberLayout;
|
||||
this.emotPager = mCurPage;
|
||||
|
||||
emotPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
if (categoryDataList != null) {
|
||||
// 显示所有贴图表情
|
||||
setCurStickerPage(position);
|
||||
if (categoryChangedCallback != null) {
|
||||
int currentCategoryChecked = pagerIndexInfo[0];// 当前那种类别被选中
|
||||
categoryChangedCallback.onCategoryChanged(currentCategoryChecked);
|
||||
}
|
||||
} else {
|
||||
// 只显示表情
|
||||
setCurEmotionPage(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset,
|
||||
int positionOffsetPixels) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
}
|
||||
});
|
||||
emotPager.setAdapter(pagerAdapter);
|
||||
emotPager.setOffscreenPageLimit(1);
|
||||
}
|
||||
|
||||
public void setCategoryDataReloadFlag() {
|
||||
isDataInitialized = false;
|
||||
}
|
||||
|
||||
public void showStickers(int index) {
|
||||
// 判断是否需要变化
|
||||
if (isDataInitialized && getPagerInfo(emotPager.getCurrentItem()) != null
|
||||
&& pagerIndexInfo[0] == index && pagerIndexInfo[1] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.categoryIndex = index;
|
||||
showStickerGridView();
|
||||
}
|
||||
|
||||
public void showEmojis() {
|
||||
showEmojiGridView();
|
||||
}
|
||||
|
||||
private int getCategoryPageCount(StickerCategory category) {
|
||||
if (category == null) {
|
||||
return (int) Math.ceil(EmojiManager.getDisplayCount() / (float) EMOJI_PER_PAGE);
|
||||
} else {
|
||||
if (category.hasStickers()) {
|
||||
List<StickerItem> stickers = category.getStickers();
|
||||
return (int) Math.ceil(stickers.size() / (float) STICKER_PER_PAGE);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setCurPage(int page, int pageCount) {
|
||||
int hasCount = pageNumberLayout.getChildCount();
|
||||
int forMax = Math.max(hasCount, pageCount);
|
||||
|
||||
ImageView imgCur = null;
|
||||
for (int i = 0; i < forMax; i++) {
|
||||
if (pageCount <= hasCount) {
|
||||
if (i >= pageCount) {
|
||||
pageNumberLayout.getChildAt(i).setVisibility(View.GONE);
|
||||
continue;
|
||||
} else {
|
||||
imgCur = (ImageView) pageNumberLayout.getChildAt(i);
|
||||
}
|
||||
} else {
|
||||
if (i < hasCount) {
|
||||
imgCur = (ImageView) pageNumberLayout.getChildAt(i);
|
||||
} else {
|
||||
imgCur = new ImageView(context);
|
||||
imgCur.setBackgroundResource(R.drawable.nim_view_pager_indicator_selector);
|
||||
pageNumberLayout.addView(imgCur);
|
||||
}
|
||||
}
|
||||
|
||||
imgCur.setId(i);
|
||||
imgCur.setSelected(i == page); // 判断当前页码来更新
|
||||
imgCur.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ******************************** 表情 *******************************
|
||||
*/
|
||||
private void showEmojiGridView() {
|
||||
pageCount = (int) Math.ceil(EmojiManager.getDisplayCount() / (float) EMOJI_PER_PAGE);
|
||||
pagerAdapter.notifyDataSetChanged();
|
||||
resetEmotionPager();
|
||||
}
|
||||
|
||||
private void resetEmotionPager() {
|
||||
setCurEmotionPage(0);
|
||||
emotPager.setCurrentItem(0, false);
|
||||
}
|
||||
|
||||
private void setCurEmotionPage(int position) {
|
||||
setCurPage(position, pageCount);
|
||||
}
|
||||
|
||||
public OnItemClickListener emojiListener = new OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
|
||||
int position = emotPager.getCurrentItem();
|
||||
int pos = position; // 如果只有表情,那么用默认方式计算
|
||||
if (categoryDataList != null && categoryPageNumberList != null) {
|
||||
// 包含贴图
|
||||
getPagerInfo(position);
|
||||
pos = pagerIndexInfo[1];
|
||||
}
|
||||
|
||||
int index = arg2 + pos * EMOJI_PER_PAGE;
|
||||
|
||||
if (listener != null) {
|
||||
int count = EmojiManager.getDisplayCount();
|
||||
if (arg2 == EMOJI_PER_PAGE || index >= count) {
|
||||
listener.onEmojiSelected("/DEL");
|
||||
} else {
|
||||
String text = EmojiManager.getDisplayText((int) arg3);
|
||||
if (!TextUtils.isEmpty(text)) {
|
||||
listener.onEmojiSelected(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* ******************************** 贴图 *******************************
|
||||
*/
|
||||
|
||||
private void showStickerGridView() {
|
||||
initData();
|
||||
pagerAdapter.notifyDataSetChanged();
|
||||
|
||||
// 计算起始的pager index
|
||||
int position = 0;
|
||||
for (int i = 0; i < categoryPageNumberList.size(); i++) {
|
||||
if (i == categoryIndex) {
|
||||
break;
|
||||
}
|
||||
position += categoryPageNumberList.get(i);
|
||||
}
|
||||
|
||||
setCurStickerPage(position);
|
||||
emotPager.setCurrentItem(position, false);
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
if (isDataInitialized) {//数据已经初始化,未变动不重新加载数据
|
||||
return;
|
||||
}
|
||||
|
||||
if (categoryDataList == null) {
|
||||
categoryDataList = new ArrayList<>();
|
||||
}
|
||||
|
||||
if (categoryPageNumberList == null) {
|
||||
categoryPageNumberList = new ArrayList<>();
|
||||
}
|
||||
|
||||
categoryDataList.clear();
|
||||
categoryPageNumberList.clear();
|
||||
|
||||
final StickerManager manager = StickerManager.getInstance();
|
||||
|
||||
categoryDataList.add(null); // 表情
|
||||
categoryPageNumberList.add(getCategoryPageCount(null));
|
||||
|
||||
List<StickerCategory> categories = manager.getCategories();
|
||||
|
||||
categoryDataList.addAll(categories); // 贴图
|
||||
for (StickerCategory c : categories) {
|
||||
categoryPageNumberList.add(getCategoryPageCount(c));
|
||||
}
|
||||
|
||||
pageCount = 0;//总页数
|
||||
for (Integer count : categoryPageNumberList) {
|
||||
pageCount += count;
|
||||
}
|
||||
|
||||
isDataInitialized = true;
|
||||
}
|
||||
|
||||
// 给定pager中的索引,返回categoryIndex和positionInCategory
|
||||
private int[] getPagerInfo(int position) {
|
||||
if (categoryDataList == null || categoryPageNumberList == null) {
|
||||
return pagerIndexInfo;
|
||||
}
|
||||
|
||||
int cIndex = categoryIndex;
|
||||
int startIndex = 0;
|
||||
int pageNumberPerCategory = 0;
|
||||
for (int i = 0; i < categoryPageNumberList.size(); i++) {
|
||||
pageNumberPerCategory = categoryPageNumberList.get(i);
|
||||
if (position < startIndex + pageNumberPerCategory) {
|
||||
cIndex = i;
|
||||
break;
|
||||
}
|
||||
startIndex += pageNumberPerCategory;
|
||||
}
|
||||
|
||||
this.pagerIndexInfo[0] = cIndex;
|
||||
this.pagerIndexInfo[1] = position - startIndex;
|
||||
|
||||
return pagerIndexInfo;
|
||||
}
|
||||
|
||||
private void setCurStickerPage(int position) {
|
||||
getPagerInfo(position);
|
||||
int categoryIndex = pagerIndexInfo[0];
|
||||
int pageIndexInCategory = pagerIndexInfo[1];
|
||||
int categoryPageCount = categoryPageNumberList.get(categoryIndex);
|
||||
|
||||
setCurPage(pageIndexInCategory, categoryPageCount);
|
||||
}
|
||||
|
||||
public void setCategoryChangCheckedCallback(IEmoticonCategoryChanged callback) {
|
||||
this.categoryChangedCallback = callback;
|
||||
}
|
||||
|
||||
private OnItemClickListener stickerListener = new OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
|
||||
int position = emotPager.getCurrentItem();
|
||||
getPagerInfo(position);
|
||||
int cIndex = pagerIndexInfo[0];
|
||||
int pos = pagerIndexInfo[1];
|
||||
StickerCategory c = categoryDataList.get(cIndex);
|
||||
int index = arg2 + pos * STICKER_PER_PAGE; // 在category中贴图的index
|
||||
|
||||
if (index >= c.getStickers().size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
StickerManager manager = StickerManager.getInstance();
|
||||
List<StickerItem> stickers = c.getStickers();
|
||||
StickerItem sticker = stickers.get(index);
|
||||
StickerCategory real = manager.getCategory(sticker.getCategory());
|
||||
|
||||
if (real == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
listener.onStickerSelected(sticker.getCategory(), sticker.getName());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* ***************************** PagerAdapter ****************************
|
||||
*/
|
||||
private class EmoticonViewPaperAdapter extends PagerAdapter {
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object object) {
|
||||
return view == object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return pageCount == 0 ? 1 : pageCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
StickerCategory category;
|
||||
int pos;
|
||||
if (categoryDataList != null && categoryDataList.size() > 0 && categoryPageNumberList != null
|
||||
&& categoryPageNumberList.size() > 0) {
|
||||
// 显示所有贴图&表情
|
||||
getPagerInfo(position);
|
||||
int cIndex = pagerIndexInfo[0];
|
||||
category = categoryDataList.get(cIndex);
|
||||
pos = pagerIndexInfo[1];
|
||||
} else {
|
||||
// 只显示表情
|
||||
category = null;
|
||||
pos = position;
|
||||
}
|
||||
|
||||
if (category == null) {
|
||||
pageNumberLayout.setVisibility(View.VISIBLE);
|
||||
GridView gridView = new GridView(context);
|
||||
gridView.setOnItemClickListener(emojiListener);
|
||||
gridView.setAdapter(new EmojiAdapter(context, pos * EMOJI_PER_PAGE));
|
||||
gridView.setNumColumns(7);
|
||||
gridView.setHorizontalSpacing(5);
|
||||
gridView.setVerticalSpacing(5);
|
||||
gridView.setGravity(Gravity.CENTER);
|
||||
gridView.setSelector(R.drawable.nim_emoji_item_selector);
|
||||
container.addView(gridView);
|
||||
return gridView;
|
||||
} else {
|
||||
pageNumberLayout.setVisibility(View.VISIBLE);
|
||||
GridView gridView = new GridView(context);
|
||||
gridView.setPadding(10, 0, 10, 0);
|
||||
gridView.setOnItemClickListener(stickerListener);
|
||||
gridView.setAdapter(new StickerAdapter(context, category, pos * STICKER_PER_PAGE));
|
||||
gridView.setNumColumns(4);
|
||||
gridView.setHorizontalSpacing(5);
|
||||
gridView.setGravity(Gravity.CENTER);
|
||||
gridView.setSelector(R.drawable.nim_emoji_item_selector);
|
||||
container.addView(gridView);
|
||||
return gridView;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
View layout = (View) object;
|
||||
container.removeView(layout);
|
||||
}
|
||||
|
||||
public int getItemPosition(Object object) {
|
||||
return POSITION_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.fengliyan.uikit.flingswipe;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
/**
|
||||
* Created by dionysis_lorentzos on 6/8/14
|
||||
* for package com.lorentzos.swipecards
|
||||
* and project Swipe cards.
|
||||
* Use with caution dinausaurs might appear!
|
||||
*/
|
||||
abstract class BaseFlingAdapterView extends AdapterView {
|
||||
|
||||
private int heightMeasureSpec;
|
||||
private int widthMeasureSpec;
|
||||
|
||||
|
||||
|
||||
public BaseFlingAdapterView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public BaseFlingAdapterView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public BaseFlingAdapterView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(int i) {
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
this.widthMeasureSpec = widthMeasureSpec;
|
||||
this.heightMeasureSpec = heightMeasureSpec;
|
||||
}
|
||||
|
||||
|
||||
public int getWidthMeasureSpec() {
|
||||
return widthMeasureSpec;
|
||||
}
|
||||
|
||||
public int getHeightMeasureSpec() {
|
||||
return heightMeasureSpec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
package com.fengliyan.uikit.flingswipe;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.graphics.PointF;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
|
||||
/**
|
||||
* Created by dionysis_lorentzos on 5/8/14
|
||||
* for package com.lorentzos.swipecards
|
||||
* and project Swipe cards.
|
||||
* Use with caution dinausaurs might appear!
|
||||
*/
|
||||
|
||||
|
||||
public class FlingCardListener implements View.OnTouchListener {
|
||||
|
||||
private static final String TAG = FlingCardListener.class.getSimpleName();
|
||||
private static final int INVALID_POINTER_ID = -1;
|
||||
|
||||
private final float objectX;
|
||||
private final float objectY;
|
||||
private final int objectH;
|
||||
private final int objectW;
|
||||
private final int parentWidth;
|
||||
private final FlingListener mFlingListener;
|
||||
private final Object dataObject;
|
||||
private final float halfWidth;
|
||||
private float BASE_ROTATION_DEGREES;
|
||||
|
||||
private float aPosX;
|
||||
private float aPosY;
|
||||
private float aDownTouchX;
|
||||
private float aDownTouchY;
|
||||
|
||||
// The active pointer is the one currently moving our object.
|
||||
private int mActivePointerId = INVALID_POINTER_ID;
|
||||
private View frame = null;
|
||||
|
||||
|
||||
private final int TOUCH_ABOVE = 0;
|
||||
private final int TOUCH_BELOW = 1;
|
||||
private int touchPosition;
|
||||
private final Object obj = new Object();
|
||||
private boolean isAnimationRunning = false;
|
||||
private float MAX_COS = (float) Math.cos(Math.toRadians(45));
|
||||
|
||||
|
||||
public FlingCardListener(View frame, Object itemAtPosition, FlingListener flingListener) {
|
||||
this(frame, itemAtPosition, 15f, flingListener);
|
||||
}
|
||||
|
||||
public FlingCardListener(View frame, Object itemAtPosition, float rotation_degrees, FlingListener flingListener) {
|
||||
super();
|
||||
this.frame = frame;
|
||||
this.objectX = frame.getX();
|
||||
this.objectY = frame.getY();
|
||||
this.objectH = frame.getHeight();
|
||||
this.objectW = frame.getWidth();
|
||||
this.halfWidth = objectW / 2f;
|
||||
this.dataObject = itemAtPosition;
|
||||
this.parentWidth = ((ViewGroup) frame.getParent()).getWidth();
|
||||
this.BASE_ROTATION_DEGREES = rotation_degrees;
|
||||
this.mFlingListener = flingListener;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
|
||||
switch (event.getAction() & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
|
||||
// from http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
|
||||
// Save the ID of this pointer
|
||||
|
||||
mActivePointerId = event.getPointerId(0);
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
boolean success = false;
|
||||
try {
|
||||
x = event.getX(mActivePointerId);
|
||||
y = event.getY(mActivePointerId);
|
||||
success = true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.w(TAG, "Exception in onTouch(view, event) : " + mActivePointerId, e);
|
||||
}
|
||||
if (success) {
|
||||
// Remember where we started
|
||||
aDownTouchX = x;
|
||||
aDownTouchY = y;
|
||||
//to prevent an initial jump of the magnifier, aposX and aPosY must
|
||||
//have the values from the magnifier frame
|
||||
if (aPosX == 0) {
|
||||
aPosX = frame.getX();
|
||||
}
|
||||
if (aPosY == 0) {
|
||||
aPosY = frame.getY();
|
||||
}
|
||||
|
||||
if (y < objectH / 2) {
|
||||
touchPosition = TOUCH_ABOVE;
|
||||
} else {
|
||||
touchPosition = TOUCH_BELOW;
|
||||
}
|
||||
}
|
||||
|
||||
view.getParent().requestDisallowInterceptTouchEvent(true);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
mActivePointerId = INVALID_POINTER_ID;
|
||||
resetCardViewOnStack();
|
||||
|
||||
view.getParent().requestDisallowInterceptTouchEvent(false);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
// Extract the index of the pointer that left the touch sensor
|
||||
final int pointerIndex = (event.getAction() &
|
||||
MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
final int pointerId = event.getPointerId(pointerIndex);
|
||||
if (pointerId == mActivePointerId) {
|
||||
// This was our active pointer going up. Choose a new
|
||||
// active pointer and adjust accordingly.
|
||||
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
|
||||
mActivePointerId = event.getPointerId(newPointerIndex);
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
|
||||
// Log.e("move", "move......");
|
||||
// Find the index of the active pointer and fetch its position
|
||||
final int pointerIndexMove = event.findPointerIndex(mActivePointerId);
|
||||
final float xMove = event.getX(pointerIndexMove);
|
||||
final float yMove = event.getY(pointerIndexMove);
|
||||
|
||||
|
||||
//from http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
|
||||
// Calculate the distance moved
|
||||
final float dx = xMove - aDownTouchX;
|
||||
final float dy = yMove - aDownTouchY;
|
||||
|
||||
// Move the frame
|
||||
aPosX += dx;
|
||||
aPosY += dy;
|
||||
// Log.e("x,y", aPosX + "," + aPosY);
|
||||
|
||||
// calculate the rotation degrees
|
||||
float distobjectX = aPosX - objectX;
|
||||
float rotation = BASE_ROTATION_DEGREES * 2.f * distobjectX / parentWidth;
|
||||
if (touchPosition == TOUCH_BELOW) {
|
||||
rotation = -rotation;
|
||||
}
|
||||
|
||||
//in this area would be code for doing something with the view as the frame moves.
|
||||
frame.setX(aPosX);
|
||||
frame.setY(aPosY);
|
||||
// frame.setRotation(rotation);
|
||||
mFlingListener.onMoveXY(aPosX, aPosY);
|
||||
mFlingListener.onScroll(getScrollProgressPercent());
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL: {
|
||||
mActivePointerId = INVALID_POINTER_ID;
|
||||
view.getParent().requestDisallowInterceptTouchEvent(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private float getScrollProgressPercent() {
|
||||
if (movedBeyondLeftBorder()) {
|
||||
return -1f;
|
||||
} else if (movedBeyondRightBorder()) {
|
||||
return 1f;
|
||||
} else {
|
||||
float zeroToOneValue = (aPosX + halfWidth - leftBorder()) / (rightBorder() - leftBorder());
|
||||
return zeroToOneValue * 2f - 1f;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean resetCardViewOnStack() {
|
||||
if (movedBeyondLeftBorder()) {
|
||||
// Left Swipe
|
||||
onSelected(true, getExitPoint(-objectW), 100);
|
||||
mFlingListener.onScroll(-1.0f);
|
||||
mFlingListener.onMoveXY(0, 0);
|
||||
} else if (movedBeyondRightBorder()) {
|
||||
// Right Swipe
|
||||
onSelected(false, getExitPoint(parentWidth), 100);
|
||||
mFlingListener.onScroll(1.0f);
|
||||
mFlingListener.onMoveXY(0, 0);
|
||||
} else {
|
||||
mFlingListener.onMoveXY(0, 0);
|
||||
float abslMoveDistance = Math.abs(aPosX - objectX);
|
||||
//距离不够回到起点
|
||||
aPosX = 0;
|
||||
aPosY = 0;
|
||||
aDownTouchX = 0;
|
||||
aDownTouchY = 0;
|
||||
Log.e("v",frame.getX()+"");
|
||||
frame.animate()
|
||||
.setDuration(200)
|
||||
.setInterpolator(new OvershootInterpolator(1.5f))
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
super.onAnimationRepeat(animation);
|
||||
Log.e("v",frame.getX()+"");
|
||||
Log.e("v",frame.getX()+"");
|
||||
Log.e("v",frame.getX()+"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationPause(Animator animation) {
|
||||
super.onAnimationPause(animation);
|
||||
Log.e("v",frame.getX()+"");
|
||||
}
|
||||
})
|
||||
.x(objectX)
|
||||
.y(objectY)
|
||||
.rotation(0);
|
||||
mFlingListener.onScroll(0.0f);
|
||||
if (abslMoveDistance < 4.0) {
|
||||
mFlingListener.onClick(dataObject);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean movedBeyondLeftBorder() {
|
||||
return aPosX + halfWidth < leftBorder();
|
||||
}
|
||||
|
||||
private boolean movedBeyondRightBorder() {
|
||||
return aPosX + halfWidth > rightBorder();
|
||||
}
|
||||
|
||||
|
||||
public float leftBorder() {
|
||||
return parentWidth / 5.f;
|
||||
}
|
||||
|
||||
public float rightBorder() {
|
||||
return 3 * parentWidth / 5.f;
|
||||
}
|
||||
|
||||
|
||||
public void onSelected(final boolean isLeft,
|
||||
float exitY, long duration) {
|
||||
|
||||
isAnimationRunning = true;
|
||||
float exitX;
|
||||
if (isLeft) {
|
||||
exitX = -objectW - getRotationWidthOffset();
|
||||
} else {
|
||||
exitX = parentWidth + getRotationWidthOffset();
|
||||
}
|
||||
|
||||
this.frame.animate()
|
||||
.setDuration(duration)
|
||||
.setInterpolator(new AccelerateInterpolator())
|
||||
.x(exitX)
|
||||
.y(exitY)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (isLeft) {
|
||||
mFlingListener.onCardExited();
|
||||
mFlingListener.leftExit(dataObject);
|
||||
} else {
|
||||
mFlingListener.onCardExited();
|
||||
mFlingListener.rightExit(dataObject);
|
||||
}
|
||||
isAnimationRunning = false;
|
||||
}
|
||||
})
|
||||
.rotation(getExitRotation(isLeft));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts a default left exit animation.
|
||||
*/
|
||||
public void selectLeft() {
|
||||
if (!isAnimationRunning)
|
||||
onSelected(true, objectY, 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a default right exit animation.
|
||||
*/
|
||||
public void selectRight() {
|
||||
if (!isAnimationRunning)
|
||||
onSelected(false, objectY, 200);
|
||||
}
|
||||
|
||||
|
||||
private float getExitPoint(int exitXPoint) {
|
||||
float[] x = new float[2];
|
||||
x[0] = objectX;
|
||||
x[1] = aPosX;
|
||||
|
||||
float[] y = new float[2];
|
||||
y[0] = objectY;
|
||||
y[1] = aPosY;
|
||||
|
||||
LinearRegression regression = new LinearRegression(x, y);
|
||||
|
||||
//Your typical y = ax+b linear regression
|
||||
return (float) regression.slope() * exitXPoint + (float) regression.intercept();
|
||||
}
|
||||
|
||||
private float getExitRotation(boolean isLeft) {
|
||||
float rotation = BASE_ROTATION_DEGREES * 2.f * (parentWidth - objectX) / parentWidth;
|
||||
if (touchPosition == TOUCH_BELOW) {
|
||||
rotation = -rotation;
|
||||
}
|
||||
if (isLeft) {
|
||||
rotation = -rotation;
|
||||
}
|
||||
return rotation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When the object rotates it's width becomes bigger.
|
||||
* The maximum width is at 45 degrees.
|
||||
* <p/>
|
||||
* The below method calculates the width offset of the rotation.
|
||||
*/
|
||||
private float getRotationWidthOffset() {
|
||||
return objectW / MAX_COS - objectW;
|
||||
}
|
||||
|
||||
|
||||
public void setRotationDegrees(float degrees) {
|
||||
this.BASE_ROTATION_DEGREES = degrees;
|
||||
}
|
||||
|
||||
public boolean isTouching() {
|
||||
return this.mActivePointerId != INVALID_POINTER_ID;
|
||||
}
|
||||
|
||||
public PointF getLastPoint() {
|
||||
return new PointF(this.aPosX, this.aPosY);
|
||||
}
|
||||
|
||||
protected interface FlingListener {
|
||||
void onCardExited();
|
||||
|
||||
void leftExit(Object dataObject);
|
||||
|
||||
void rightExit(Object dataObject);
|
||||
|
||||
void onClick(Object dataObject);
|
||||
|
||||
void onScroll(float scrollProgressPercent);
|
||||
|
||||
void onMoveXY(float moveX, float moveY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.fengliyan.uikit.flingswipe;
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* Compilation: javac LinearRegression.java
|
||||
* Execution: java LinearRegression
|
||||
*
|
||||
* Compute least squares solution to y = beta * x + alpha.
|
||||
* Simple linear regression.
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* The <tt>LinearRegression</tt> class performs a simple linear regression
|
||||
* on an set of <em>N</em> data points (<em>y<sub>i</sub></em>, <em>x<sub>i</sub></em>).
|
||||
* That is, it fits a straight line <em>y</em> = α + β <em>x</em>,
|
||||
* (where <em>y</em> is the response variable, <em>x</em> is the predictor variable,
|
||||
* α is the <em>y-intercept</em>, and β is the <em>slope</em>)
|
||||
* that minimizes the sum of squared residuals of the linear regression model.
|
||||
* It also computes associated statistics, including the coefficient of
|
||||
* determination <em>R</em><sup>2</sup> and the standard deviation of the
|
||||
* estimates for the slope and <em>y</em>-intercept.
|
||||
*
|
||||
* @author Robert Sedgewick
|
||||
* @author Kevin Wayne
|
||||
*/
|
||||
class LinearRegression {
|
||||
private final int N;
|
||||
private final double alpha, beta;
|
||||
private final double R2;
|
||||
private final double svar, svar0, svar1;
|
||||
|
||||
/**
|
||||
* Performs a linear regression on the data points <tt>(y[i], x[i])</tt>.
|
||||
* @param x the values of the predictor variable
|
||||
* @param y the corresponding values of the response variable
|
||||
* @throws IllegalArgumentException if the lengths of the two arrays are not equal
|
||||
*/
|
||||
public LinearRegression(float[] x, float[] y) {
|
||||
if (x.length != y.length) {
|
||||
throw new IllegalArgumentException("array lengths are not equal");
|
||||
}
|
||||
N = x.length;
|
||||
|
||||
// first pass
|
||||
double sumx = 0.0, sumy = 0.0, sumx2 = 0.0;
|
||||
for (int i = 0; i < N; i++) sumx += x[i];
|
||||
for (int i = 0; i < N; i++) sumx2 += x[i]*x[i];
|
||||
for (int i = 0; i < N; i++) sumy += y[i];
|
||||
double xbar = sumx / N;
|
||||
double ybar = sumy / N;
|
||||
|
||||
// second pass: compute summary statistics
|
||||
double xxbar = 0.0, yybar = 0.0, xybar = 0.0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
xxbar += (x[i] - xbar) * (x[i] - xbar);
|
||||
yybar += (y[i] - ybar) * (y[i] - ybar);
|
||||
xybar += (x[i] - xbar) * (y[i] - ybar);
|
||||
}
|
||||
beta = xybar / xxbar;
|
||||
alpha = ybar - beta * xbar;
|
||||
|
||||
// more statistical analysis
|
||||
double rss = 0.0; // residual sum of squares
|
||||
double ssr = 0.0; // regression sum of squares
|
||||
for (int i = 0; i < N; i++) {
|
||||
double fit = beta*x[i] + alpha;
|
||||
rss += (fit - y[i]) * (fit - y[i]);
|
||||
ssr += (fit - ybar) * (fit - ybar);
|
||||
}
|
||||
|
||||
int degreesOfFreedom = N-2;
|
||||
R2 = ssr / yybar;
|
||||
svar = rss / degreesOfFreedom;
|
||||
svar1 = svar / xxbar;
|
||||
svar0 = svar/N + xbar*xbar*svar1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <em>y</em>-intercept α of the best of the best-fit line <em>y</em> = α + β <em>x</em>.
|
||||
* @return the <em>y</em>-intercept α of the best-fit line <em>y = α + β x</em>
|
||||
*/
|
||||
public double intercept() {
|
||||
return alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the slope β of the best of the best-fit line <em>y</em> = α + β <em>x</em>.
|
||||
* @return the slope β of the best-fit line <em>y</em> = α + β <em>x</em>
|
||||
*/
|
||||
public double slope() {
|
||||
return beta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the coefficient of determination <em>R</em><sup>2</sup>.
|
||||
* @return the coefficient of determination <em>R</em><sup>2</sup>, which is a real number between 0 and 1
|
||||
*/
|
||||
public double R2() {
|
||||
return R2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the standard error of the estimate for the intercept.
|
||||
* @return the standard error of the estimate for the intercept
|
||||
*/
|
||||
public double interceptStdErr() {
|
||||
return Math.sqrt(svar0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the standard error of the estimate for the slope.
|
||||
* @return the standard error of the estimate for the slope
|
||||
*/
|
||||
public double slopeStdErr() {
|
||||
return Math.sqrt(svar1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expected response <tt>y</tt> given the value of the predictor
|
||||
* variable <tt>x</tt>.
|
||||
* @param x the value of the predictor variable
|
||||
* @return the expected response <tt>y</tt> given the value of the predictor
|
||||
* variable <tt>x</tt>
|
||||
*/
|
||||
public double predict(double x) {
|
||||
return beta*x + alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the simple linear regression model.
|
||||
* @return a string representation of the simple linear regression model,
|
||||
* including the best-fit line and the coefficient of determination <em>R</em><sup>2</sup>
|
||||
*/
|
||||
public String toString() {
|
||||
String s = "";
|
||||
s += String.format("%.2f N + %.2f", slope(), intercept());
|
||||
return s + " (R^2 = " + String.format("%.3f", R2()) + ")";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,387 @@
|
||||
package com.fengliyan.uikit.flingswipe;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.DataSetObserver;
|
||||
import android.graphics.PointF;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.Adapter;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
|
||||
/**
|
||||
* Created by dionysis_lorentzos on 5/8/14
|
||||
* for package com.lorentzos.swipecards
|
||||
* and project Swipe cards.
|
||||
* Use with caution dinosaurs might appear!
|
||||
*/
|
||||
|
||||
public class SwipeFlingAdapterView extends BaseFlingAdapterView {
|
||||
|
||||
private int MAX_VISIBLE = 4;
|
||||
private int MIN_ADAPTER_STACK = 6;
|
||||
private float ROTATION_DEGREES = 15.f;
|
||||
private float ITEM_SMALL_WIDTH = 20;
|
||||
private float ITEM_SMALL_HIGH = 32;
|
||||
|
||||
private Adapter mAdapter;
|
||||
private int LAST_OBJECT_IN_STACK = 0;
|
||||
private onFlingListener mFlingListener;
|
||||
private AdapterDataSetObserver mDataSetObserver;
|
||||
private boolean mInLayout = false;
|
||||
private View mActiveCard = null;
|
||||
private OnItemClickListener mOnItemClickListener;
|
||||
private FlingCardListener flingCardListener;
|
||||
private PointF mLastTouchPoint;
|
||||
|
||||
private float p = 0f;
|
||||
|
||||
public SwipeFlingAdapterView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public SwipeFlingAdapterView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.SwipeFlingStyle);
|
||||
}
|
||||
|
||||
public SwipeFlingAdapterView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeFlingAdapterView, defStyle, 0);
|
||||
MAX_VISIBLE = a.getInt(R.styleable.SwipeFlingAdapterView_max_visible, MAX_VISIBLE);
|
||||
MIN_ADAPTER_STACK = a.getInt(R.styleable.SwipeFlingAdapterView_min_adapter_stack, MIN_ADAPTER_STACK);
|
||||
ROTATION_DEGREES = a.getFloat(R.styleable.SwipeFlingAdapterView_rotation_degrees, ROTATION_DEGREES);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
private float dpToPx(int sp) {
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, sp, getContext().getResources().getDisplayMetrics());
|
||||
}
|
||||
|
||||
/**
|
||||
* A shortcut method to set both the listeners and the adapter.
|
||||
*
|
||||
* @param context The activity context which extends onFlingListener, OnItemClickListener or both
|
||||
* @param mAdapter The adapter you have to set.
|
||||
*/
|
||||
public void init(final Context context, Adapter mAdapter) {
|
||||
if (context instanceof onFlingListener) {
|
||||
mFlingListener = (onFlingListener) context;
|
||||
} else {
|
||||
throw new RuntimeException("Activity does not implement SwipeFlingAdapterView.onFlingListener");
|
||||
}
|
||||
if (context instanceof OnItemClickListener) {
|
||||
mOnItemClickListener = (OnItemClickListener) context;
|
||||
}
|
||||
setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getSelectedView() {
|
||||
return mActiveCard;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void requestLayout() {
|
||||
if (!mInLayout) {
|
||||
super.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
// if we don't have an adapter, we don't need to do anything
|
||||
if (mAdapter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mInLayout = true;
|
||||
final int adapterCount = mAdapter.getCount();
|
||||
|
||||
if (adapterCount == 0) {
|
||||
removeAllViewsInLayout();
|
||||
} else {
|
||||
View topCard = getChildAt(LAST_OBJECT_IN_STACK);
|
||||
if (mActiveCard != null && topCard != null && topCard == mActiveCard) {
|
||||
if (this.flingCardListener.isTouching()) {
|
||||
PointF lastPoint = this.flingCardListener.getLastPoint();
|
||||
if (this.mLastTouchPoint == null || !this.mLastTouchPoint.equals(lastPoint)) {
|
||||
this.mLastTouchPoint = lastPoint;
|
||||
removeViewsInLayout(0, LAST_OBJECT_IN_STACK);
|
||||
layoutChildren(1, adapterCount, 3);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Reset the UI and set top view listener
|
||||
removeAllViewsInLayout();
|
||||
layoutChildren(0, adapterCount, 3);
|
||||
setTopView();
|
||||
}
|
||||
}
|
||||
|
||||
mInLayout = false;
|
||||
|
||||
if (adapterCount <= MIN_ADAPTER_STACK) mFlingListener.onAdapterAboutToEmpty(adapterCount);
|
||||
}
|
||||
|
||||
|
||||
private void layoutChildren(int startingIndex, int adapterCount, int count) {
|
||||
|
||||
while (startingIndex < Math.min(adapterCount, 4)) {
|
||||
View newUnderChild = mAdapter.getView(startingIndex, null, this);
|
||||
if (newUnderChild.getVisibility() != GONE) {
|
||||
makeAndAddView(startingIndex, newUnderChild);
|
||||
LAST_OBJECT_IN_STACK = startingIndex;
|
||||
}
|
||||
startingIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转改变view 大小
|
||||
*
|
||||
* @param child
|
||||
* @param index
|
||||
*/
|
||||
private void adjustChildView(View child, int index) {
|
||||
int n;
|
||||
if (index > 1)
|
||||
n = 2;
|
||||
else
|
||||
n = index;
|
||||
if (index == 3 && p > 0.5f) {
|
||||
n = index;
|
||||
}
|
||||
child.offsetTopAndBottom((int) (dpToPx((int) ITEM_SMALL_HIGH) * (n - p)));
|
||||
child.setScaleX(1 - 0.1f * (n - p));
|
||||
child.setScaleY(1 - 0.1f * (n - p));
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制子View
|
||||
*
|
||||
* @param index
|
||||
* @param child
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
private void makeAndAddView(int index, View child) {
|
||||
|
||||
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams();
|
||||
addViewInLayout(child, 0, lp, true);
|
||||
|
||||
final boolean needToMeasure = child.isLayoutRequested();
|
||||
if (needToMeasure) {
|
||||
int childWidthSpec = getChildMeasureSpec(getWidthMeasureSpec(), getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, lp.width);
|
||||
int childHeightSpec = getChildMeasureSpec(getHeightMeasureSpec(), getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin, lp.height);
|
||||
child.measure(childWidthSpec, childHeightSpec);
|
||||
} else {
|
||||
cleanupLayoutState(child);
|
||||
}
|
||||
|
||||
|
||||
int w = child.getMeasuredWidth();
|
||||
int h = child.getMeasuredHeight();
|
||||
int gravity = lp.gravity;
|
||||
if (gravity == -1) {
|
||||
gravity = Gravity.TOP | Gravity.START;
|
||||
}
|
||||
int layoutDirection = getLayoutDirection();
|
||||
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
|
||||
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
|
||||
|
||||
int childLeft;
|
||||
int childTop;
|
||||
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
|
||||
case Gravity.CENTER_HORIZONTAL:
|
||||
childLeft = (getWidth() + getPaddingLeft() - getPaddingRight() - w) / 2 + lp.leftMargin - lp.rightMargin;
|
||||
break;
|
||||
case Gravity.END:
|
||||
childLeft = getWidth() + getPaddingRight() - w - lp.rightMargin;
|
||||
break;
|
||||
case Gravity.START:
|
||||
default:
|
||||
int l = 0;
|
||||
childLeft = getPaddingLeft() + lp.leftMargin + l;
|
||||
break;
|
||||
}
|
||||
switch (verticalGravity) {
|
||||
case Gravity.CENTER_VERTICAL:
|
||||
childTop = (getHeight() + getPaddingTop() - getPaddingBottom() - h) / 2 +
|
||||
lp.topMargin - lp.bottomMargin;
|
||||
break;
|
||||
case Gravity.BOTTOM:
|
||||
childTop = getHeight() - getPaddingBottom() - h - lp.bottomMargin;
|
||||
break;
|
||||
case Gravity.TOP:
|
||||
default:
|
||||
int top = 0;
|
||||
childTop = getPaddingTop() + lp.topMargin + top;
|
||||
break;
|
||||
}
|
||||
|
||||
child.layout(childLeft, childTop, childLeft + w, childTop + h);
|
||||
adjustChildView(child, index);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the top view and add the fling listener
|
||||
*/
|
||||
private void setTopView() {
|
||||
if (getChildCount() > 0) {
|
||||
|
||||
mActiveCard = getChildAt(LAST_OBJECT_IN_STACK);
|
||||
if (mActiveCard != null) {
|
||||
|
||||
flingCardListener = new FlingCardListener(mActiveCard, mAdapter.getItem(0),
|
||||
ROTATION_DEGREES, new FlingCardListener.FlingListener() {
|
||||
|
||||
@Override
|
||||
public void onCardExited() {
|
||||
mActiveCard = null;
|
||||
p = 0f;
|
||||
mFlingListener.removeFirstObjectInAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void leftExit(Object dataObject) {
|
||||
p = 0f;
|
||||
mFlingListener.onLeftCardExit(dataObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rightExit(Object dataObject) {
|
||||
mFlingListener.onRightCardExit(dataObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(Object dataObject) {
|
||||
if (mOnItemClickListener != null)
|
||||
mOnItemClickListener.onItemClicked(0, dataObject);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScroll(float scrollProgressPercent) {
|
||||
mFlingListener.onScroll(scrollProgressPercent);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoveXY(float moveX, float moveY) {
|
||||
float mX = (int) Math.abs(moveX);
|
||||
float mY = (int) Math.abs(moveY);
|
||||
if (mX > 50 || mY > 50) {
|
||||
float m = Math.max(mX, mY);
|
||||
p = (m - 50f) / 500f;
|
||||
if (p > 1f) {
|
||||
p = 1f;
|
||||
}
|
||||
} else {
|
||||
p = 0f;
|
||||
}
|
||||
requestLayout();
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
mActiveCard.setOnTouchListener(flingCardListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FlingCardListener getTopCardListener() throws NullPointerException {
|
||||
if (flingCardListener == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return flingCardListener;
|
||||
}
|
||||
|
||||
public void setMaxVisible(int MAX_VISIBLE) {
|
||||
this.MAX_VISIBLE = MAX_VISIBLE;
|
||||
}
|
||||
|
||||
public void setMinStackInAdapter(int MIN_ADAPTER_STACK) {
|
||||
this.MIN_ADAPTER_STACK = MIN_ADAPTER_STACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Adapter getAdapter() {
|
||||
return mAdapter;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setAdapter(Adapter adapter) {
|
||||
if (mAdapter != null && mDataSetObserver != null) {
|
||||
mAdapter.unregisterDataSetObserver(mDataSetObserver);
|
||||
mDataSetObserver = null;
|
||||
}
|
||||
|
||||
mAdapter = adapter;
|
||||
|
||||
if (mAdapter != null && mDataSetObserver == null) {
|
||||
mDataSetObserver = new AdapterDataSetObserver();
|
||||
mAdapter.registerDataSetObserver(mDataSetObserver);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFlingListener(onFlingListener onFlingListener) {
|
||||
this.mFlingListener = onFlingListener;
|
||||
}
|
||||
|
||||
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
|
||||
this.mOnItemClickListener = onItemClickListener;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public LayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||
return new FrameLayout.LayoutParams(getContext(), attrs);
|
||||
}
|
||||
|
||||
|
||||
private class AdapterDataSetObserver extends DataSetObserver {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidated() {
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClicked(int itemPosition, Object dataObject);
|
||||
}
|
||||
|
||||
public interface onFlingListener {
|
||||
void removeFirstObjectInAdapter();
|
||||
|
||||
void onLeftCardExit(Object dataObject);
|
||||
|
||||
void onRightCardExit(Object dataObject);
|
||||
|
||||
void onAdapterAboutToEmpty(int itemsInAdapter);
|
||||
|
||||
void onScroll(float scrollProgressPercent);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,930 @@
|
||||
package com.fengliyan.uikit.mzbanner;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Scroller;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class CoverFlowView extends RelativeLayout {
|
||||
|
||||
public enum CoverFlowGravity {
|
||||
TOP, BOTTOM, CENTER_VERTICAL
|
||||
}
|
||||
|
||||
public enum CoverFlowLayoutMode {
|
||||
MATCH_PARENT, WRAP_CONTENT
|
||||
}
|
||||
|
||||
protected CoverFlowGravity mGravity;
|
||||
|
||||
protected CoverFlowLayoutMode mLayoutMode;
|
||||
|
||||
private Scroller mScroller;
|
||||
/**
|
||||
* To store reflections need to remove
|
||||
*/
|
||||
private ArrayList<View> removeViewArray;
|
||||
|
||||
private SparseArray<View> showViewArray;
|
||||
|
||||
private int paddingLeft;
|
||||
private int paddingRight;
|
||||
private int paddingTop;
|
||||
private int paddingBottom;
|
||||
|
||||
private int mWidth; // 控件的宽度
|
||||
|
||||
private float reflectHeightFraction;
|
||||
private int reflectGap;
|
||||
|
||||
private int mChildHeight; // child的高度
|
||||
private int mChildTranslateY;
|
||||
//private int mReflectionTranslateY;
|
||||
|
||||
private int mVisibleChildCount; // 一屏显示的图片数量
|
||||
|
||||
protected int VISIBLE_VIEWS = 3; // the visible views left and right 左右两边显示的个数
|
||||
|
||||
private ICoverFlowAdapter mAdapter;
|
||||
|
||||
private float mOffset;
|
||||
//private int mLastOffset;
|
||||
|
||||
private final int ALPHA_DATUM = 76; // 基础alphaֵ
|
||||
private int STANDARD_ALPHA;
|
||||
// 基础缩放值
|
||||
//private static final float CARD_SCALE = 0.15f;
|
||||
|
||||
private static float MOVE_POS_MULTIPLE = 3.0f;
|
||||
private static final int TOUCH_MINIMUM_MOVE = 5;
|
||||
private static final float MOVE_SPEED_MULTIPLE = 1;
|
||||
private static final float MAX_SPEED = 6.0f;
|
||||
private static final float FRICTION = 10.0f;
|
||||
|
||||
private VelocityTracker mVelocity;
|
||||
|
||||
private int firstIndex = 0;
|
||||
|
||||
public CoverFlowView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CoverFlowView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initAttributes(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public CoverFlowView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initAttributes(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
private void initAttributes(Context context, AttributeSet attrs) {
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ImageCoverFlowView);
|
||||
int totalVisibleChildren = a.getInt(
|
||||
R.styleable.ImageCoverFlowView_visibleImage, 3);
|
||||
if (totalVisibleChildren % 2 == 0) { // 一屏幕必须是奇数显示
|
||||
throw new IllegalArgumentException("visible image must be an odd number");
|
||||
}
|
||||
|
||||
VISIBLE_VIEWS = totalVisibleChildren >> 1; // 计算出左右两两边的显示个数
|
||||
|
||||
reflectHeightFraction = a.getFraction(
|
||||
R.styleable.ImageCoverFlowView_reflectionHeight, 100, 0, 0.0f);
|
||||
|
||||
if (reflectHeightFraction > 100) {
|
||||
reflectHeightFraction = 100;
|
||||
}
|
||||
reflectHeightFraction /= 100;
|
||||
reflectGap = a.getDimensionPixelSize(
|
||||
R.styleable.ImageCoverFlowView_reflectionGap, 0);
|
||||
|
||||
mGravity = CoverFlowGravity.values()[a.getInt(
|
||||
R.styleable.ImageCoverFlowView_coverflowGravity,
|
||||
CoverFlowGravity.CENTER_VERTICAL.ordinal())];
|
||||
|
||||
mLayoutMode = CoverFlowLayoutMode.values()[a.getInt(
|
||||
R.styleable.ImageCoverFlowView_coverflowLayoutMode,
|
||||
CoverFlowLayoutMode.WRAP_CONTENT.ordinal())];
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
removeAllViews();
|
||||
setWillNotDraw(false);
|
||||
setClickable(true);
|
||||
|
||||
if (mScroller == null) {
|
||||
mScroller = new Scroller(getContext(), new AccelerateDecelerateInterpolator());
|
||||
}
|
||||
if (showViewArray == null) {
|
||||
showViewArray = new SparseArray<View>();
|
||||
} else {
|
||||
showViewArray.clear();
|
||||
}
|
||||
if (removeViewArray == null) {
|
||||
removeViewArray = new ArrayList<View>();
|
||||
} else {
|
||||
removeViewArray.clear();
|
||||
}
|
||||
|
||||
firstIndex = 0;
|
||||
mChildHeight = 0;
|
||||
mOffset = 0;
|
||||
//mLastOffset = -1;
|
||||
|
||||
isFirstIn = true;
|
||||
lastMid = 1;
|
||||
isChange = true;
|
||||
|
||||
// 计算透明度
|
||||
STANDARD_ALPHA = (255 - ALPHA_DATUM) / VISIBLE_VIEWS;
|
||||
|
||||
if (mGravity == null) {
|
||||
mGravity = CoverFlowGravity.CENTER_VERTICAL;
|
||||
}
|
||||
|
||||
if (mLayoutMode == null) {
|
||||
mLayoutMode = CoverFlowLayoutMode.WRAP_CONTENT;
|
||||
}
|
||||
|
||||
// 一屏 显示的图片数量
|
||||
int visibleCount = (VISIBLE_VIEWS << 1) + 1;
|
||||
|
||||
for (int i = 0; i < visibleCount && mAdapter != null && i < mAdapter.getCount(); ++i) {
|
||||
|
||||
View convertView = null;
|
||||
if (removeViewArray.size() > 0) {
|
||||
convertView = removeViewArray.remove(0);
|
||||
}
|
||||
View view = mAdapter.getView(i, convertView, this);
|
||||
showViewArray.put(i, view);
|
||||
addView(view);
|
||||
}
|
||||
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
if (mAdapter == null || showViewArray.size() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
paddingLeft = getPaddingLeft();
|
||||
paddingRight = getPaddingRight();
|
||||
paddingTop = getPaddingTop();
|
||||
paddingBottom = getPaddingBottom();
|
||||
|
||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
// 一屏显示的图片数量
|
||||
int visibleCount = (VISIBLE_VIEWS << 1) + 1;
|
||||
|
||||
// 控件高度
|
||||
int availableHeight = heightSize - paddingTop - paddingBottom;
|
||||
|
||||
int maxChildTotalHeight = 0;
|
||||
for (int i = 0; i < getChildCount() && i < visibleCount && i < showViewArray.size(); ++i) {
|
||||
|
||||
//View view = showViewArray.get(i+firstIndex);
|
||||
View view = getChildAt(i);
|
||||
measureChild(view, widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
//final int childHeight = ScreenUtil.dp2px(getContext(), 110);
|
||||
final int childHeight = view.getMeasuredHeight();
|
||||
final int childTotalHeight = (int) (childHeight + childHeight
|
||||
* reflectHeightFraction + reflectGap);
|
||||
|
||||
// 孩子的最大高度
|
||||
maxChildTotalHeight = (maxChildTotalHeight < childTotalHeight) ? childTotalHeight
|
||||
: maxChildTotalHeight;
|
||||
}
|
||||
|
||||
// 如果控件模式为确切值 或者 最大时
|
||||
if (heightMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.AT_MOST) {
|
||||
// if height which parent provided is less than child need, scale
|
||||
// child height to parent provide
|
||||
// 如果控件高度小于孩子控件高度,则缩放孩子高度为控件高度
|
||||
if (availableHeight < maxChildTotalHeight) {
|
||||
mChildHeight = availableHeight;
|
||||
} else {
|
||||
// if larger than, depends on layout mode
|
||||
// if layout mode is match_parent, scale child height to parent
|
||||
// provide
|
||||
// 如果是填充父窗体模式 则将孩子的高度 设为控件高度
|
||||
if (mLayoutMode == CoverFlowLayoutMode.MATCH_PARENT) {
|
||||
mChildHeight = availableHeight;
|
||||
// if layout mode is wrap_content, keep child's original
|
||||
// height
|
||||
// 如果是包裹内容 则将孩子的高度设为孩子允许的最大高度
|
||||
} else if (mLayoutMode == CoverFlowLayoutMode.WRAP_CONTENT) {
|
||||
mChildHeight = maxChildTotalHeight;
|
||||
|
||||
// adjust parent's height
|
||||
// 计算出控件的高度
|
||||
if (heightMode == MeasureSpec.AT_MOST) {
|
||||
heightSize = mChildHeight + paddingTop + paddingBottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// height mode is unspecified
|
||||
// 如果空间高度 没有明确定义
|
||||
// 如果孩子的模式为填充父窗体
|
||||
if (mLayoutMode == CoverFlowLayoutMode.MATCH_PARENT) {
|
||||
mChildHeight = availableHeight;
|
||||
// 如果孩子的模式为包裹内容
|
||||
} else if (mLayoutMode == CoverFlowLayoutMode.WRAP_CONTENT) {
|
||||
mChildHeight = maxChildTotalHeight;
|
||||
|
||||
// 计算出控件的高度
|
||||
// adjust parent's height
|
||||
heightSize = mChildHeight + paddingTop + paddingBottom;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust movement in y-axis according to gravity
|
||||
// 计算出孩子的原点 Y坐标
|
||||
if (mGravity == CoverFlowGravity.CENTER_VERTICAL) {// 竖直居中
|
||||
mChildTranslateY = (heightSize >> 1) - (mChildHeight >> 1);
|
||||
} else if (mGravity == CoverFlowGravity.TOP) {// 顶部对齐
|
||||
mChildTranslateY = paddingTop;
|
||||
} else if (mGravity == CoverFlowGravity.BOTTOM) {// 底部对齐
|
||||
mChildTranslateY = heightSize - paddingBottom - mChildHeight;
|
||||
}
|
||||
|
||||
//mReflectionTranslateY = (int) (mChildTranslateY + mChildHeight - mChildHeight
|
||||
// * reflectHeightFraction);
|
||||
|
||||
setMeasuredDimension(widthSize, heightSize);
|
||||
mVisibleChildCount = visibleCount;
|
||||
mWidth = widthSize;
|
||||
|
||||
}
|
||||
|
||||
boolean isFirstIn = true; // 第一次初始化该控件
|
||||
int lastMid = 1;
|
||||
boolean isChange = true;
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
if (mAdapter == null || mAdapter.getCount() <= 0 || showViewArray.size() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final float offset = mOffset;
|
||||
int i = 0;
|
||||
int mid = (int) Math.floor(offset + 0.5);
|
||||
|
||||
//右边孩子的数量
|
||||
int rightChild = (mVisibleChildCount % 2 == 0) ? (mVisibleChildCount >> 1) - 1
|
||||
: mVisibleChildCount >> 1;
|
||||
//左边孩子的数量
|
||||
int leftChild = mVisibleChildCount >> 1;
|
||||
|
||||
if (!isFirstIn) {
|
||||
if (lastMid + 1 == mid) {
|
||||
int actuallyPositionStart = getActuallyPosition(lastMid - leftChild);
|
||||
View view = showViewArray.get(actuallyPositionStart);
|
||||
showViewArray.remove(actuallyPositionStart);
|
||||
removeViewArray.add(view);
|
||||
removeView(view);
|
||||
|
||||
View convertView = null;
|
||||
if (removeViewArray.size() > 0) {
|
||||
convertView = removeViewArray.remove(0);
|
||||
}
|
||||
int actuallyPositionEnd = getActuallyPosition(mid + rightChild);
|
||||
|
||||
View viewItem = mAdapter.getView(actuallyPositionEnd, convertView, this);
|
||||
|
||||
showViewArray.put(actuallyPositionEnd, viewItem);
|
||||
addView(viewItem);
|
||||
|
||||
isChange = true;
|
||||
|
||||
} else if (lastMid - 1 == mid) {
|
||||
int actuallyPositionEnd = getActuallyPosition(lastMid + rightChild);
|
||||
View view = showViewArray.get(actuallyPositionEnd);
|
||||
showViewArray.remove(actuallyPositionEnd);
|
||||
removeViewArray.add(view);
|
||||
removeView(view);
|
||||
|
||||
View convertView = null;
|
||||
if (removeViewArray.size() > 0) {
|
||||
convertView = removeViewArray.remove(0);
|
||||
}
|
||||
int actuallyPositionStart = getActuallyPosition(mid - leftChild);
|
||||
|
||||
View viewItem = mAdapter.getView(actuallyPositionStart, convertView, this);
|
||||
|
||||
showViewArray.put(actuallyPositionStart, viewItem);
|
||||
addView(viewItem, 0);
|
||||
|
||||
isChange = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
isFirstIn = false;
|
||||
}
|
||||
|
||||
lastMid = mid;
|
||||
|
||||
// draw the left children
|
||||
// 计算左边孩子的位置
|
||||
int startPos = mid - leftChild;
|
||||
for (i = startPos; i < mid; ++i) {
|
||||
// 计算左边孩子的位置
|
||||
View child = layoutLeftChild(i, i - offset);
|
||||
|
||||
if (isChange) {
|
||||
int actuallyPosition = getActuallyPosition(i);
|
||||
mAdapter.getData(child, actuallyPosition);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算 右边 和 中间
|
||||
int endPos = mid + rightChild;
|
||||
|
||||
int j = -VISIBLE_VIEWS;
|
||||
for (i = endPos; i >= mid; --i, j = j + 2) {
|
||||
// 计算右边和中间孩子的位置
|
||||
View child = layoutRightChild(i + j, i - offset);
|
||||
|
||||
if (isChange) {
|
||||
int actuallyPosition = getActuallyPosition(i);
|
||||
mAdapter.getData(child, actuallyPosition);
|
||||
}
|
||||
}
|
||||
|
||||
isChange = false;
|
||||
}
|
||||
|
||||
private View layoutLeftChild(int position, float offset) {
|
||||
//获取实际的position
|
||||
int actuallyPosition = getActuallyPosition(position);
|
||||
View child = showViewArray.get(actuallyPosition);
|
||||
if (child != null) {
|
||||
makeChildTransfromer(child, actuallyPosition, offset);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
private View layoutRightChild(int position, float offset) {
|
||||
|
||||
//获取实际的position
|
||||
int actuallyPosition = getActuallyPosition(position);
|
||||
View child = showViewArray.get(actuallyPosition);
|
||||
if (child != null) {
|
||||
makeChildTransfromer(child, actuallyPosition, offset);
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对 bitmap 进行伪 3d 变换
|
||||
*
|
||||
* @param child
|
||||
* @param position
|
||||
* @param offset
|
||||
*/
|
||||
private void makeChildTransfromer(View child, int position, float offset) {
|
||||
//child.layout(0, 0, ScreenUtil.dp2px(getContext(), 200),ScreenUtil.dp2px(getContext(), 110));
|
||||
child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
|
||||
|
||||
float scale = 0;
|
||||
scale = 1 - Math.abs(offset) * 0.25f;
|
||||
|
||||
// 延x轴移动的距离应该根据center图片决定
|
||||
float translateX = 0;
|
||||
|
||||
final int originalChildHeight = (int) (mChildHeight - mChildHeight
|
||||
* reflectHeightFraction - reflectGap);
|
||||
|
||||
//final int childTotalHeight = (int) (child.getHeight()
|
||||
// + child.getHeight() * reflectHeightFraction + reflectGap);
|
||||
|
||||
int height = child.getHeight();
|
||||
int width = child.getWidth();
|
||||
if (height == 0) {
|
||||
height = originalChildHeight;
|
||||
}
|
||||
if (width == 0) {
|
||||
width = originalChildHeight;
|
||||
}
|
||||
final float originalChildHeightScale = (float) originalChildHeight / height;
|
||||
|
||||
final float childHeightScale = originalChildHeightScale * scale;
|
||||
|
||||
final int childWidth = (int) (width * childHeightScale);
|
||||
|
||||
final int centerChildWidth = (int) (width * originalChildHeightScale);
|
||||
|
||||
//final int centerChildWidth = (int) (child.getWidth() * childHeightScale);
|
||||
|
||||
int leftSpace = ((mWidth >> 1) - paddingLeft) - (centerChildWidth >> 1);
|
||||
int rightSpace = (((mWidth >> 1) - paddingRight) - (centerChildWidth >> 1));
|
||||
|
||||
//计算出水平方向的x坐标
|
||||
if (offset <= 0)
|
||||
translateX = ((float) leftSpace / VISIBLE_VIEWS)
|
||||
* (VISIBLE_VIEWS + offset) + paddingLeft;
|
||||
|
||||
else
|
||||
translateX = mWidth - ((float) rightSpace / VISIBLE_VIEWS)
|
||||
* (VISIBLE_VIEWS - offset) - childWidth - paddingRight;
|
||||
|
||||
//根据offset 算出透明度
|
||||
float alpha = 254 - Math.abs(offset) * STANDARD_ALPHA;
|
||||
|
||||
child.setAlpha(0);
|
||||
if (alpha < 0) {
|
||||
alpha = 0;
|
||||
} else if (alpha > 254) {
|
||||
alpha = 254;
|
||||
}
|
||||
child.setAlpha(alpha / 254.0f);
|
||||
|
||||
float adjustedChildTranslateY = 0;
|
||||
if (childHeightScale == Float.POSITIVE_INFINITY || childHeightScale == Float.NEGATIVE_INFINITY || Float.isNaN(childHeightScale)) {
|
||||
Log.i("TAG", "makeChildTransfromer: childHeightScale------->" + childHeightScale);
|
||||
// removeAllViews();
|
||||
// invalidate();
|
||||
init();
|
||||
} else {
|
||||
ObjectAnimator anim1 = ObjectAnimator.ofFloat(child, "scaleX", 1.0f, childHeightScale);
|
||||
ObjectAnimator anim2 = ObjectAnimator.ofFloat(child, "scaleY", 1.0f, childHeightScale);
|
||||
AnimatorSet animSet = new AnimatorSet();
|
||||
animSet.setDuration(0);
|
||||
//两个动画同时执行
|
||||
animSet.playTogether(anim1, anim2);
|
||||
child.setPivotX(0);
|
||||
child.setPivotY(height / 2);
|
||||
//显示的调用invalidate
|
||||
child.invalidate();
|
||||
animSet.setTarget(child);
|
||||
animSet.start();
|
||||
child.setTranslationX(translateX);
|
||||
child.setTranslationY(mChildTranslateY + adjustedChildTranslateY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取顶部Item position
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getTopViewPosition() {
|
||||
return getActuallyPosition(lastMid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取顶部Item View
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public View getTopView() {
|
||||
return showViewArray.get(getActuallyPosition(VISIBLE_VIEWS + lastMid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert draw-index to index in adapter
|
||||
*
|
||||
* @param position position to draw
|
||||
* @return
|
||||
*/
|
||||
private int getActuallyPosition(int position) {
|
||||
int max = mAdapter.getCount();
|
||||
position += VISIBLE_VIEWS;
|
||||
while (position < 0 || position >= max) {
|
||||
if (position < 0) {
|
||||
position += max;
|
||||
} else if (position >= max) {
|
||||
position -= max;
|
||||
}
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setAdapter(ICoverFlowAdapter adapter) {
|
||||
mAdapter = adapter;
|
||||
init();
|
||||
}
|
||||
|
||||
public ICoverFlowAdapter getAdapter() {
|
||||
return mAdapter;
|
||||
}
|
||||
|
||||
private boolean onTouchMove = false; //是否正在执行触摸移动逻辑
|
||||
|
||||
private View touchViewItem = null;
|
||||
private boolean isOnTopView = false;
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (getParent() != null) {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
|
||||
if (isOnAnimator) {
|
||||
return false;
|
||||
}
|
||||
int action = event.getAction();
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
onTouchMove = true;
|
||||
if (mScroller.computeScrollOffset()) {
|
||||
mScroller.abortAnimation();
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
touchBegan(event);
|
||||
touchViewItem = getTopView();
|
||||
isOnTopView = inRangeOfView(touchViewItem, event);
|
||||
if (isOnTopView) {
|
||||
sendLongClickAction();
|
||||
}
|
||||
return true;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
touchMoved(event);
|
||||
removeLongClickAction();
|
||||
touchViewItem = null;
|
||||
isOnTopView = false;
|
||||
return true;
|
||||
case MotionEvent.ACTION_UP:
|
||||
removeLongClickAction();
|
||||
if (isOnTopView && touchViewItem == getTopView()
|
||||
&& inRangeOfView(touchViewItem, event)) {
|
||||
|
||||
if (mTopViewClickListener != null) {
|
||||
mTopViewClickListener.onClick(getTopViewPosition(), getTopView());
|
||||
}
|
||||
}
|
||||
touchViewItem = null;
|
||||
isOnTopView = false;
|
||||
touchEnded(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Runnable longClickRunnable = null;
|
||||
|
||||
/**
|
||||
* 发送长点击事件 Runnable
|
||||
*/
|
||||
private void sendLongClickAction() {
|
||||
removeLongClickAction();
|
||||
longClickRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
touchViewItem = null;
|
||||
isOnTopView = false;
|
||||
if (mTopViewLongClickListener != null) {
|
||||
mTopViewLongClickListener.onLongClick(getTopViewPosition(), getTopView());
|
||||
}
|
||||
}
|
||||
};
|
||||
postDelayed(longClickRunnable, 600);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除长点击事件 Runnable
|
||||
*/
|
||||
private void removeLongClickAction() {
|
||||
if (longClickRunnable != null) {
|
||||
removeCallbacks(longClickRunnable);
|
||||
longClickRunnable = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean inRangeOfView(View view, MotionEvent ev) {
|
||||
Rect frame = new Rect();
|
||||
view.getHitRect(frame);
|
||||
if (frame.contains((int) ev.getX(), (int) ev.getY())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private OnTopViewClickListener mTopViewClickListener;
|
||||
private OnTopViewLongClickListener mTopViewLongClickListener;
|
||||
|
||||
/**
|
||||
* 设置 TopView 的点击监听
|
||||
*
|
||||
* @param topViewClickListener
|
||||
*/
|
||||
public void setOnTopViewClickListener(OnTopViewClickListener topViewClickListener) {
|
||||
this.mTopViewClickListener = topViewClickListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 TopView 的点击监听
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public OnTopViewClickListener getOnTopViewClickListener() {
|
||||
return this.mTopViewClickListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 TopView 的长点击监听
|
||||
*
|
||||
* @param topViewLongClickListener
|
||||
*/
|
||||
public void setOnTopViewLongClickListener(OnTopViewLongClickListener topViewLongClickListener) {
|
||||
this.mTopViewLongClickListener = topViewLongClickListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 TopView 的长点击监听
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public OnTopViewLongClickListener getOnTopViewLongClickListener() {
|
||||
return this.mTopViewLongClickListener;
|
||||
}
|
||||
|
||||
public interface OnTopViewClickListener {
|
||||
|
||||
void onClick(int position, View itemView);
|
||||
|
||||
}
|
||||
|
||||
public interface OnTopViewLongClickListener {
|
||||
|
||||
void onLongClick(int position, View itemView);
|
||||
|
||||
}
|
||||
|
||||
private boolean mTouchMoved;
|
||||
private float mTouchStartPos;
|
||||
private float mTouchStartX;
|
||||
private float mTouchStartY;
|
||||
|
||||
private long mStartTime;
|
||||
private float mStartOffset;
|
||||
|
||||
private void touchBegan(MotionEvent event) {
|
||||
endAnimation();
|
||||
|
||||
float x = event.getX();
|
||||
mTouchStartX = x;
|
||||
mTouchStartY = event.getY();
|
||||
mStartTime = AnimationUtils.currentAnimationTimeMillis();
|
||||
mStartOffset = mOffset;
|
||||
|
||||
mTouchMoved = false;
|
||||
|
||||
mTouchStartPos = (x / mWidth) * MOVE_POS_MULTIPLE - 5;
|
||||
mTouchStartPos /= 2;
|
||||
|
||||
mVelocity = VelocityTracker.obtain();
|
||||
mVelocity.addMovement(event);
|
||||
}
|
||||
|
||||
private Runnable mAnimationRunnable;
|
||||
|
||||
private void endAnimation() {
|
||||
if (mAnimationRunnable != null) {
|
||||
mOffset = (float) Math.floor(mOffset + 0.5);
|
||||
|
||||
invalidate();
|
||||
requestLayout();
|
||||
|
||||
removeCallbacks(mAnimationRunnable);
|
||||
mAnimationRunnable = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void touchMoved(MotionEvent event) {
|
||||
float pos = (event.getX() / mWidth) * MOVE_POS_MULTIPLE - 5;
|
||||
pos /= 2;
|
||||
|
||||
if (!mTouchMoved) {
|
||||
float dx = Math.abs(event.getX() - mTouchStartX);
|
||||
float dy = Math.abs(event.getY() - mTouchStartY);
|
||||
if (dx < TOUCH_MINIMUM_MOVE && dy < TOUCH_MINIMUM_MOVE)
|
||||
return;
|
||||
mTouchMoved = true;
|
||||
}
|
||||
|
||||
mOffset = mStartOffset + mTouchStartPos - pos;
|
||||
|
||||
invalidate();
|
||||
requestLayout();
|
||||
mVelocity.addMovement(event);
|
||||
}
|
||||
|
||||
private void touchEnded(MotionEvent event) {
|
||||
float pos = (event.getX() / mWidth) * MOVE_POS_MULTIPLE - 5;
|
||||
pos /= 2;
|
||||
|
||||
if (mTouchMoved || (mOffset - Math.floor(mOffset)) != 0) {
|
||||
mStartOffset += mTouchStartPos - pos;
|
||||
mOffset = mStartOffset;
|
||||
|
||||
mVelocity.addMovement(event);
|
||||
|
||||
mVelocity.computeCurrentVelocity(1000);
|
||||
float speed = mVelocity.getXVelocity();
|
||||
|
||||
speed = (speed / mWidth) * MOVE_SPEED_MULTIPLE;
|
||||
if (speed > MAX_SPEED)
|
||||
speed = MAX_SPEED;
|
||||
else if (speed < -MAX_SPEED)
|
||||
speed = -MAX_SPEED;
|
||||
|
||||
startAnimation(-speed);
|
||||
} else {
|
||||
Log.e(VIEW_LOG_TAG, " touch ==>" + event.getX() + " , " + event.getY());
|
||||
onTouchMove = false;
|
||||
}
|
||||
|
||||
mVelocity.clear();
|
||||
mVelocity.recycle();
|
||||
}
|
||||
|
||||
private float mStartSpeed;
|
||||
private float mDuration;
|
||||
|
||||
private void startAnimation(float speed) {
|
||||
if (mAnimationRunnable != null) {
|
||||
onTouchMove = false;
|
||||
return;
|
||||
}
|
||||
|
||||
float delta = speed * speed / (FRICTION * 2);
|
||||
if (speed < 0)
|
||||
delta = -delta;
|
||||
|
||||
float nearest = mStartOffset + delta;
|
||||
nearest = (float) Math.floor(nearest + 0.5f);
|
||||
|
||||
mStartSpeed = (float) Math.sqrt(Math.abs(nearest - mStartOffset) * FRICTION * 2);
|
||||
if (nearest < mStartOffset)
|
||||
mStartSpeed = -mStartSpeed;
|
||||
|
||||
mDuration = Math.abs(mStartSpeed / FRICTION);
|
||||
mStartTime = AnimationUtils.currentAnimationTimeMillis();
|
||||
|
||||
mAnimationRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
driveAnimation();
|
||||
}
|
||||
};
|
||||
post(mAnimationRunnable);
|
||||
}
|
||||
|
||||
private void driveAnimation() {
|
||||
float elapsed = (AnimationUtils.currentAnimationTimeMillis() - mStartTime) / 1000.0f;
|
||||
if (elapsed >= mDuration) {
|
||||
endAnimation();
|
||||
onTouchMove = false;
|
||||
} else {
|
||||
updateAnimationAtElapsed(elapsed);
|
||||
post(mAnimationRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAnimationAtElapsed(float elapsed) {
|
||||
if (elapsed > mDuration)
|
||||
elapsed = mDuration;
|
||||
|
||||
float delta = Math.abs(mStartSpeed) * elapsed - FRICTION * elapsed * elapsed / 2;
|
||||
if (mStartSpeed < 0)
|
||||
delta = -delta;
|
||||
|
||||
mOffset = mStartOffset + delta;
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeScroll() {
|
||||
super.computeScroll();
|
||||
|
||||
// 算出移动到某一个虚拟点时 mOffset 的值,然后 invalidate 重绘
|
||||
if (mScroller.computeScrollOffset()) {
|
||||
final int currX = mScroller.getCurrX();
|
||||
mOffset = (float) currX / 100;
|
||||
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isOnAnimator = false; //是否正在进行点击切换动画
|
||||
|
||||
/**
|
||||
* 翻到前页
|
||||
*/
|
||||
public void gotoPrevious() {
|
||||
doAnimator(-1.0f);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 前进到后一页
|
||||
*/
|
||||
public void gotoForward() {
|
||||
doAnimator(1.0f);
|
||||
}
|
||||
|
||||
public void doAnimator(float target) {
|
||||
if (isOnAnimator || onTouchMove) { //如果正在执行点击切换动画 或者 正在执行触摸移动
|
||||
return;
|
||||
}
|
||||
isOnAnimator = true;
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(mOffset, mOffset + target);
|
||||
animator.setDuration(300).start();
|
||||
animator.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
animator.addUpdateListener(new AnimatorUpdateListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
mOffset = (Float) animation.getAnimatedValue();
|
||||
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
});
|
||||
animator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
isOnAnimator = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Runnable task = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
gotoForward();
|
||||
postDelayed(task, delayTime);
|
||||
}
|
||||
};
|
||||
private int delayTime;
|
||||
|
||||
public void startAutoPlay(int delayTime) {
|
||||
this.delayTime = delayTime;
|
||||
removeCallbacks(task);
|
||||
postDelayed(task, delayTime);
|
||||
}
|
||||
|
||||
public void stopAutoPlay() {
|
||||
removeCallbacks(task);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
|
||||
return true; // 禁止滑动
|
||||
}
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.fengliyan.uikit.mzbanner;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Created by zhouwei on 17/8/16.
|
||||
*/
|
||||
|
||||
public class CustomViewPager extends ViewPager {
|
||||
private ArrayList<Integer> childCenterXAbs = new ArrayList<>();
|
||||
private SparseArray<Integer> childIndex = new SparseArray<>();
|
||||
|
||||
public CustomViewPager(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CustomViewPager(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init(){
|
||||
setClipToPadding(false);
|
||||
setOverScrollMode(OVER_SCROLL_NEVER);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param childCount
|
||||
* @param n
|
||||
* @return 第n个位置的child 的绘制索引
|
||||
*/
|
||||
@Override
|
||||
protected int getChildDrawingOrder(int childCount, int n) {
|
||||
if (n == 0 || childIndex.size() != childCount) {
|
||||
childCenterXAbs.clear();
|
||||
childIndex.clear();
|
||||
int viewCenterX = getViewCenterX(this);
|
||||
for (int i = 0; i < childCount; ++i) {
|
||||
int indexAbs = Math.abs(viewCenterX - getViewCenterX(getChildAt(i)));
|
||||
//两个距离相同,后来的那个做自增,从而保持abs不同
|
||||
if (childIndex.get(indexAbs) != null) {
|
||||
++indexAbs;
|
||||
}
|
||||
childCenterXAbs.add(indexAbs);
|
||||
childIndex.append(indexAbs, i);
|
||||
}
|
||||
Collections.sort(childCenterXAbs);//1,0,2 0,1,2
|
||||
}
|
||||
//那个item距离中心点远一些,就先draw它。(最近的就是中间放大的item,最后draw)
|
||||
return childIndex.get(childCenterXAbs.get(childCount - 1 - n));
|
||||
}
|
||||
|
||||
private int getViewCenterX(View view) {
|
||||
int[] array = new int[2];
|
||||
view.getLocationOnScreen(array);
|
||||
return array[0] + view.getWidth() / 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.fengliyan.uikit.mzbanner;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
public interface ICoverFlowAdapter {
|
||||
|
||||
int getCount();
|
||||
|
||||
Object getItem(int position);
|
||||
|
||||
long getItemId(int position);
|
||||
|
||||
View getView(int position, View convertView, ViewGroup parent);
|
||||
|
||||
void getData(View view, int position);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,741 @@
|
||||
package com.fengliyan.uikit.mzbanner;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Scroller;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.mzbanner.holder.MZHolderCreator;
|
||||
import com.fengliyan.uikit.mzbanner.holder.MZViewHolder;
|
||||
import com.fengliyan.uikit.mzbanner.transformer.CoverModeTransformer;
|
||||
import com.fengliyan.uikit.mzbanner.transformer.ScaleYTransformer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by zhouwei on 17/5/26.
|
||||
*/
|
||||
|
||||
public class MZBannerView<T> extends RelativeLayout {
|
||||
private static final String TAG = "MZBannerView";
|
||||
private CustomViewPager mViewPager;
|
||||
private MZPagerAdapter mAdapter;
|
||||
private List<T> mDatas;
|
||||
private boolean mIsAutoPlay = true;// 是否自动播放
|
||||
private int mCurrentItem = 0;//当前位置
|
||||
private Handler mHandler = new Handler();
|
||||
private int mDelayedTime = 3000;// Banner 切换时间间隔
|
||||
private ViewPagerScroller mViewPagerScroller;//控制ViewPager滑动速度的Scroller
|
||||
private boolean mIsOpenMZEffect = true;// 开启魅族Banner效果
|
||||
private boolean mIsCanLoop = true;// 是否轮播图片
|
||||
private LinearLayout mIndicatorContainer;//indicator容器
|
||||
private ArrayList<ImageView> mIndicators = new ArrayList<>();
|
||||
//mIndicatorRes[0] 为为选中,mIndicatorRes[1]为选中
|
||||
private int []mIndicatorRes= new int[]{R.drawable.indicator_normal,R.drawable.indicator_selected};
|
||||
private int mIndicatorPaddingLeft = 0;// indicator 距离左边的距离
|
||||
private int mIndicatorPaddingRight = 0;//indicator 距离右边的距离
|
||||
private int mIndicatorPaddingTop = 0;//indicator 距离上边的距离
|
||||
private int mIndicatorPaddingBottom = 0;//indicator 距离下边的距离
|
||||
private int mMZModePadding = 0;//在仿魅族模式下,由于前后显示了上下一个页面的部分,因此需要计算这部分padding
|
||||
private int mIndicatorAlign = 1;
|
||||
private ViewPager.OnPageChangeListener mOnPageChangeListener;
|
||||
private BannerPageClickListener mBannerPageClickListener;
|
||||
public enum IndicatorAlign{
|
||||
LEFT,//做对齐
|
||||
CENTER,//居中对齐
|
||||
RIGHT //右对齐
|
||||
}
|
||||
public int getCurrentIndex(){
|
||||
return mViewPager.getCurrentItem();
|
||||
}
|
||||
/**
|
||||
* 中间Page是否覆盖两边,默认覆盖
|
||||
* @return
|
||||
*/
|
||||
|
||||
|
||||
private boolean mIsMiddlePageCover = true;
|
||||
public MZBannerView(@NonNull Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public MZBannerView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
readAttrs(context,attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public MZBannerView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
readAttrs(context,attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public MZBannerView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
readAttrs(context,attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
private void readAttrs(Context context,AttributeSet attrs){
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.MZBannerView);
|
||||
mIsOpenMZEffect = typedArray.getBoolean(R.styleable.MZBannerView_open_mz_mode,true);
|
||||
mIsMiddlePageCover = typedArray.getBoolean(R.styleable.MZBannerView_middle_page_cover,true);
|
||||
mIsCanLoop = typedArray.getBoolean(R.styleable.MZBannerView_canLoop,true);
|
||||
mIndicatorAlign = typedArray.getInt(R.styleable.MZBannerView_indicatorAlign,IndicatorAlign.CENTER.ordinal());
|
||||
mIndicatorPaddingLeft = typedArray.getDimensionPixelSize(R.styleable.MZBannerView_indicatorPaddingLeft,0);
|
||||
mIndicatorPaddingRight = typedArray.getDimensionPixelSize(R.styleable.MZBannerView_indicatorPaddingRight,0);
|
||||
mIndicatorPaddingTop = typedArray.getDimensionPixelSize(R.styleable.MZBannerView_indicatorPaddingTop,0);
|
||||
mIndicatorPaddingBottom = typedArray.getDimensionPixelSize(R.styleable.MZBannerView_indicatorPaddingBottom,0);
|
||||
typedArray.recycle();
|
||||
}
|
||||
|
||||
|
||||
private void init(){
|
||||
View view = null;
|
||||
if(mIsOpenMZEffect){
|
||||
view = LayoutInflater.from(getContext()).inflate(R.layout.mz_banner_effect_layout,this,true);
|
||||
}else{
|
||||
view = LayoutInflater.from(getContext()).inflate(R.layout.mz_banner_normal_layout,this,true);
|
||||
}
|
||||
mIndicatorContainer = (LinearLayout) view.findViewById(R.id.banner_indicator_container);
|
||||
mViewPager = (CustomViewPager) view.findViewById(R.id.mzbanner_vp);
|
||||
mViewPager.setOffscreenPageLimit(4);
|
||||
|
||||
mMZModePadding = dpToPx(30);
|
||||
// 初始化Scroller
|
||||
initViewPagerScroll();
|
||||
|
||||
sureIndicatorPosition();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否开启魅族模式
|
||||
*/
|
||||
private void setOpenMZEffect(){
|
||||
// 魅族模式
|
||||
if(mIsOpenMZEffect){
|
||||
if(mIsMiddlePageCover){
|
||||
// 中间页面覆盖两边,和魅族APP 的banner 效果一样。
|
||||
mViewPager.setPageTransformer(true,new CoverModeTransformer(mViewPager));
|
||||
}else{
|
||||
// 中间页面不覆盖,页面并排,只是Y轴缩小
|
||||
mViewPager.setPageTransformer(false,new ScaleYTransformer());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* make sure the indicator
|
||||
*/
|
||||
private void sureIndicatorPosition(){
|
||||
if(mIndicatorAlign == IndicatorAlign.LEFT.ordinal()){
|
||||
setIndicatorAlign(IndicatorAlign.LEFT);
|
||||
}else if(mIndicatorAlign == IndicatorAlign.CENTER.ordinal()){
|
||||
setIndicatorAlign(IndicatorAlign.CENTER);
|
||||
}else{
|
||||
setIndicatorAlign(IndicatorAlign.RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置ViewPager的滑动速度
|
||||
* */
|
||||
private void initViewPagerScroll() {
|
||||
try {
|
||||
Field mScroller = null;
|
||||
mScroller = ViewPager.class.getDeclaredField("mScroller");
|
||||
mScroller.setAccessible(true);
|
||||
mViewPagerScroller = new ViewPagerScroller(
|
||||
mViewPager.getContext());
|
||||
mScroller.set(mViewPager, mViewPagerScroller);
|
||||
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final Runnable mLoopRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if(mIsAutoPlay){
|
||||
mCurrentItem = mViewPager.getCurrentItem();
|
||||
mCurrentItem++;
|
||||
if(mCurrentItem == mAdapter.getCount() - 1){
|
||||
mCurrentItem = 0;
|
||||
mViewPager.setCurrentItem(mCurrentItem,false);
|
||||
mHandler.postDelayed(this,mDelayedTime);
|
||||
}else{
|
||||
mViewPager.setCurrentItem(mCurrentItem);
|
||||
mHandler.postDelayed(this,mDelayedTime);
|
||||
}
|
||||
}else{
|
||||
mHandler.postDelayed(this,mDelayedTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 初始化指示器Indicator
|
||||
*/
|
||||
private void initIndicator(){
|
||||
mIndicatorContainer.removeAllViews();
|
||||
mIndicators.clear();
|
||||
for(int i=0;i<mDatas.size();i++){
|
||||
ImageView imageView = new ImageView(getContext());
|
||||
if(mIndicatorAlign == IndicatorAlign.LEFT.ordinal()){
|
||||
if(i == 0){
|
||||
int paddingLeft = mIsOpenMZEffect ? mIndicatorPaddingLeft+mMZModePadding:mIndicatorPaddingLeft;
|
||||
imageView.setPadding(paddingLeft+6,0,6,0);
|
||||
} else{
|
||||
imageView.setPadding(6,0,6,0);
|
||||
}
|
||||
|
||||
}else if(mIndicatorAlign == IndicatorAlign.RIGHT.ordinal()){
|
||||
if(i == mDatas.size() - 1){
|
||||
int paddingRight = mIsOpenMZEffect ? mMZModePadding + mIndicatorPaddingRight:mIndicatorPaddingRight;
|
||||
imageView.setPadding(6,0,6 + paddingRight,0);
|
||||
}else{
|
||||
imageView.setPadding(6,0,6,0);
|
||||
}
|
||||
|
||||
}else{
|
||||
imageView.setPadding(6,0,6,0);
|
||||
}
|
||||
|
||||
if(i == (mCurrentItem % mDatas.size())){
|
||||
imageView.setImageResource(mIndicatorRes[1]);
|
||||
}else{
|
||||
imageView.setImageResource(mIndicatorRes[0]);
|
||||
}
|
||||
|
||||
mIndicators.add(imageView);
|
||||
mIndicatorContainer.addView(imageView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
if(!mIsCanLoop){
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
switch (ev.getAction()){
|
||||
// 按住Banner的时候,停止自动轮播
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_OUTSIDE:
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
int paddingLeft = mViewPager.getLeft();
|
||||
float touchX = ev.getRawX();
|
||||
// 如果是魅族模式,去除两边的区域
|
||||
if(touchX >= paddingLeft && touchX < getScreenWidth(getContext()) - paddingLeft){
|
||||
pause();
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
start();
|
||||
break;
|
||||
}
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
public static int getScreenWidth(Context context){
|
||||
Resources resources = context.getResources();
|
||||
DisplayMetrics dm = resources.getDisplayMetrics();
|
||||
int width = dm.widthPixels;
|
||||
return width;
|
||||
}
|
||||
|
||||
/******************************************************************************************************/
|
||||
/** 对外API **/
|
||||
/******************************************************************************************************/
|
||||
/**
|
||||
* 开始轮播
|
||||
* <p>应该确保在调用用了{@link MZBannerView {@link #setPages(List, MZHolderCreator)}} 之后调用这个方法开始轮播</p>
|
||||
*/
|
||||
public void start(){
|
||||
// 如果Adapter为null, 说明还没有设置数据,这个时候不应该轮播Banner
|
||||
if(mAdapter== null){
|
||||
return;
|
||||
}
|
||||
if(mIsCanLoop){
|
||||
pause();
|
||||
// mIsAutoPlay = true;
|
||||
// mHandler.postDelayed(mLoopRunnable,mDelayedTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止轮播
|
||||
*/
|
||||
public void pause(){
|
||||
mIsAutoPlay = false;
|
||||
mHandler.removeCallbacks(mLoopRunnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否可以轮播
|
||||
* @param canLoop
|
||||
*/
|
||||
public void setCanLoop(boolean canLoop){
|
||||
mIsCanLoop = canLoop;
|
||||
if(!canLoop){
|
||||
pause();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置BannerView 的切换时间间隔
|
||||
* @param delayedTime
|
||||
*/
|
||||
public void setDelayedTime(int delayedTime) {
|
||||
mDelayedTime = delayedTime;
|
||||
}
|
||||
|
||||
public void addPageChangeListener(ViewPager.OnPageChangeListener onPageChangeListener){
|
||||
mOnPageChangeListener = onPageChangeListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Page点击事件
|
||||
* @param bannerPageClickListener {@link BannerPageClickListener}
|
||||
*/
|
||||
public void setBannerPageClickListener(BannerPageClickListener bannerPageClickListener) {
|
||||
mBannerPageClickListener = bannerPageClickListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否显示Indicator
|
||||
* @param visible true 显示Indicator,否则不显示
|
||||
*/
|
||||
public void setIndicatorVisible(boolean visible){
|
||||
if(visible){
|
||||
mIndicatorContainer.setVisibility(VISIBLE);
|
||||
}else{
|
||||
mIndicatorContainer.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set indicator padding
|
||||
* @param paddingLeft
|
||||
* @param paddingTop
|
||||
* @param paddingRight
|
||||
* @param paddingBottom
|
||||
*/
|
||||
public void setIndicatorPadding(int paddingLeft,int paddingTop,int paddingRight,int paddingBottom){
|
||||
mIndicatorPaddingLeft = paddingLeft;
|
||||
mIndicatorPaddingTop = paddingTop;
|
||||
mIndicatorPaddingRight = paddingRight;
|
||||
mIndicatorPaddingBottom = paddingBottom;
|
||||
sureIndicatorPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回ViewPager
|
||||
* @return {@link ViewPager}
|
||||
*/
|
||||
public ViewPager getViewPager() {
|
||||
return mViewPager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置indicator 图片资源
|
||||
* @param unSelectRes 未选中状态资源图片
|
||||
* @param selectRes 选中状态资源图片
|
||||
*/
|
||||
public void setIndicatorRes(@DrawableRes int unSelectRes, @DrawableRes int selectRes){
|
||||
mIndicatorRes[0]= unSelectRes;
|
||||
mIndicatorRes[1] = selectRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据,这是最重要的一个方法。
|
||||
* <p>其他的配置应该在这个方法之前调用</p>
|
||||
* @param datas Banner 展示的数据集合
|
||||
* @param mzHolderCreator ViewHolder生成器 {@link MZHolderCreator} And {@link MZViewHolder}
|
||||
*/
|
||||
public void setPages(List<T> datas, MZHolderCreator mzHolderCreator){
|
||||
if(datas == null || mzHolderCreator == null){
|
||||
return;
|
||||
}
|
||||
mDatas = datas;
|
||||
//如果在播放,就先让播放停止
|
||||
pause();
|
||||
|
||||
//增加一个逻辑:由于魅族模式会在一个页面展示前后页面的部分,因此,数据集合的长度至少为3,否则,自动为普通Banner模式
|
||||
//不管配置的:open_mz_mode 属性的值是否为true
|
||||
|
||||
if(datas.size() < 3){
|
||||
mIsOpenMZEffect = false;
|
||||
MarginLayoutParams layoutParams = (MarginLayoutParams) mViewPager.getLayoutParams();
|
||||
layoutParams.setMargins(0,0,0,0);
|
||||
mViewPager.setLayoutParams(layoutParams);
|
||||
setClipChildren(true);
|
||||
mViewPager.setClipChildren(true);
|
||||
}
|
||||
setOpenMZEffect();
|
||||
// 2017.7.20 fix:将Indicator初始化放在Adapter的初始化之前,解决更新数据变化更新时crush.
|
||||
//初始化Indicator
|
||||
initIndicator();
|
||||
|
||||
mAdapter = new MZPagerAdapter(datas,mzHolderCreator,mIsCanLoop);
|
||||
mAdapter.setUpViewViewPager(mViewPager);
|
||||
mAdapter.setPageClickListener(mBannerPageClickListener);
|
||||
|
||||
|
||||
mViewPager.clearOnPageChangeListeners();
|
||||
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
|
||||
int realPosition = position % mIndicators.size();
|
||||
if(mOnPageChangeListener!=null){
|
||||
mOnPageChangeListener.onPageScrolled(realPosition,positionOffset,positionOffsetPixels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
mCurrentItem = position;
|
||||
|
||||
|
||||
// 切换indicator
|
||||
int realSelectPosition = mCurrentItem % mIndicators.size();
|
||||
for(int i = 0;i<mDatas.size();i++){
|
||||
if(i == realSelectPosition){
|
||||
mIndicators.get(i).setImageResource(mIndicatorRes[1]);
|
||||
}else{
|
||||
mIndicators.get(i).setImageResource(mIndicatorRes[0]);
|
||||
}
|
||||
}
|
||||
// 不能直接将mOnPageChangeListener 设置给ViewPager ,否则拿到的position 是原始的position
|
||||
if(mOnPageChangeListener!=null){
|
||||
mOnPageChangeListener.onPageSelected(realSelectPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
switch (state){
|
||||
case ViewPager.SCROLL_STATE_DRAGGING:
|
||||
mIsAutoPlay = false;
|
||||
break;
|
||||
case ViewPager.SCROLL_STATE_SETTLING:
|
||||
mIsAutoPlay = true;
|
||||
break;
|
||||
|
||||
}
|
||||
if(mOnPageChangeListener!=null){
|
||||
mOnPageChangeListener.onPageScrollStateChanged(state);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Indicator 的对齐方式
|
||||
* @param indicatorAlign {@link IndicatorAlign#CENTER }{@link IndicatorAlign#LEFT }{@link IndicatorAlign#RIGHT }
|
||||
*/
|
||||
public void setIndicatorAlign(IndicatorAlign indicatorAlign) {
|
||||
mIndicatorAlign = indicatorAlign.ordinal();
|
||||
LayoutParams layoutParams = (LayoutParams) mIndicatorContainer.getLayoutParams();
|
||||
if(indicatorAlign == IndicatorAlign.LEFT){
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
|
||||
}else if(indicatorAlign == IndicatorAlign.RIGHT){
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
}else{
|
||||
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
|
||||
}
|
||||
|
||||
// 2017.8.27 添加:增加设置Indicator 的上下边距。
|
||||
|
||||
layoutParams.setMargins(0,mIndicatorPaddingTop,0,mIndicatorPaddingBottom);
|
||||
mIndicatorContainer.setLayoutParams(layoutParams);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public LinearLayout getIndicatorContainer() {
|
||||
return mIndicatorContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置ViewPager切换的速度
|
||||
* @param duration 切换动画时间
|
||||
*/
|
||||
public void setDuration(int duration){
|
||||
mViewPagerScroller.setDuration(duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否使用ViewPager默认是的切换速度
|
||||
* @param useDefaultDuration 切换动画时间
|
||||
*/
|
||||
public void setUseDefaultDuration(boolean useDefaultDuration){
|
||||
mViewPagerScroller.setUseDefaultDuration(useDefaultDuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Banner页面切换动画时间
|
||||
* @return
|
||||
*/
|
||||
public int getDuration(){
|
||||
return mViewPagerScroller.getScrollDuration();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static class MZPagerAdapter<T> extends PagerAdapter {
|
||||
private List<T> mDatas;
|
||||
private MZHolderCreator mMZHolderCreator;
|
||||
private ViewPager mViewPager;
|
||||
private boolean canLoop;
|
||||
private BannerPageClickListener mPageClickListener;
|
||||
private final int mLooperCountFactor = 500;
|
||||
|
||||
public MZPagerAdapter(List<T> datas, MZHolderCreator MZHolderCreator,boolean canLoop) {
|
||||
if(mDatas == null){
|
||||
mDatas = new ArrayList<>();
|
||||
}
|
||||
//mDatas.add(datas.get(datas.size()-1));// 加入最后一个
|
||||
for(T t:datas){
|
||||
mDatas.add(t);
|
||||
}
|
||||
// mDatas.add(datas.get(0));//在最后加入最前面一个
|
||||
mMZHolderCreator = MZHolderCreator;
|
||||
this.canLoop = canLoop;
|
||||
}
|
||||
|
||||
public void setPageClickListener(BannerPageClickListener pageClickListener) {
|
||||
mPageClickListener = pageClickListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化Adapter和设置当前选中的Item
|
||||
* @param viewPager
|
||||
*/
|
||||
public void setUpViewViewPager(ViewPager viewPager){
|
||||
mViewPager = viewPager;
|
||||
mViewPager.setAdapter(this);
|
||||
mViewPager.getAdapter().notifyDataSetChanged();
|
||||
int currentItem = canLoop ? getStartSelectItem():0;
|
||||
//设置当前选中的Item
|
||||
mViewPager.setCurrentItem(currentItem);
|
||||
}
|
||||
|
||||
private int getStartSelectItem(){
|
||||
if(getRealCount() == 0){
|
||||
return 0;
|
||||
}
|
||||
// 我们设置当前选中的位置为Integer.MAX_VALUE / 2,这样开始就能往左滑动
|
||||
// 但是要保证这个值与getRealPosition 的 余数为0,因为要从第一页开始显示
|
||||
int currentItem = getRealCount() * mLooperCountFactor / 2;
|
||||
if(currentItem % getRealCount() ==0 ){
|
||||
return currentItem;
|
||||
}
|
||||
// 直到找到从0开始的位置
|
||||
while (currentItem % getRealCount() != 0){
|
||||
currentItem++;
|
||||
}
|
||||
return currentItem;
|
||||
}
|
||||
|
||||
public void setDatas(List<T> datas) {
|
||||
mDatas = datas;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
// 2017.6.10 bug fix
|
||||
// 如果getCount 的返回值为Integer.MAX_VALUE 的话,那么在setCurrentItem的时候会ANR(除了在onCreate 调用之外)
|
||||
return canLoop ? getRealCount() * mLooperCountFactor : getRealCount();//ViewPager返回int 最大值
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object object) {
|
||||
return view == object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, final int position) {
|
||||
View view = getView(position,container);
|
||||
container.addView(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
container.removeView((View) object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishUpdate(ViewGroup container) {
|
||||
// 轮播模式才执行
|
||||
if(canLoop){
|
||||
int position = mViewPager.getCurrentItem();
|
||||
if (position == getCount() - 1) {
|
||||
position = 0;
|
||||
setCurrentItem(position);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void setCurrentItem(int position){
|
||||
try {
|
||||
mViewPager.setCurrentItem(position, false);
|
||||
}catch (IllegalStateException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取真实的Count
|
||||
* @return
|
||||
*/
|
||||
private int getRealCount(){
|
||||
return mDatas==null ? 0:mDatas.size();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param position
|
||||
* @param container
|
||||
* @return
|
||||
*/
|
||||
private View getView(int position,ViewGroup container){
|
||||
|
||||
final int realPosition = position % getRealCount();
|
||||
MZViewHolder holder =null;
|
||||
// create holder
|
||||
holder = mMZHolderCreator.createViewHolder();
|
||||
|
||||
if(holder == null){
|
||||
throw new RuntimeException("can not return a null holder");
|
||||
}
|
||||
// create View
|
||||
View view = holder.createView(container.getContext());
|
||||
|
||||
if( mDatas!=null && mDatas.size()>0){
|
||||
holder.onBind(container.getContext(),realPosition,mDatas.get(realPosition));
|
||||
}
|
||||
|
||||
// 添加点击事件
|
||||
view.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if(mPageClickListener!=null){
|
||||
mPageClickListener.onPageClick(v,realPosition);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*由于ViewPager 默认的切换速度有点快,因此用一个Scroller 来控制切换的速度
|
||||
* <p>而实际上ViewPager 切换本来就是用的Scroller来做的,因此我们可以通过反射来</p>
|
||||
* <p>获取取到ViewPager 的 mScroller 属性,然后替换成我们自己的Scroller</p>
|
||||
*/
|
||||
public static class ViewPagerScroller extends Scroller{
|
||||
private int mDuration = 800;// ViewPager默认的最大Duration 为600,我们默认稍微大一点。值越大越慢。
|
||||
private boolean mIsUseDefaultDuration = false;
|
||||
|
||||
public ViewPagerScroller(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ViewPagerScroller(Context context, Interpolator interpolator) {
|
||||
super(context, interpolator);
|
||||
}
|
||||
|
||||
public ViewPagerScroller(Context context, Interpolator interpolator, boolean flywheel) {
|
||||
super(context, interpolator, flywheel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScroll(int startX, int startY, int dx, int dy) {
|
||||
super.startScroll(startX, startY, dx, dy,mDuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
|
||||
super.startScroll(startX, startY, dx, dy, mIsUseDefaultDuration?duration:mDuration);
|
||||
}
|
||||
|
||||
public void setUseDefaultDuration(boolean useDefaultDuration) {
|
||||
mIsUseDefaultDuration = useDefaultDuration;
|
||||
}
|
||||
|
||||
public boolean isUseDefaultDuration() {
|
||||
return mIsUseDefaultDuration;
|
||||
}
|
||||
|
||||
public void setDuration(int duration) {
|
||||
mDuration = duration;
|
||||
}
|
||||
|
||||
|
||||
public int getScrollDuration() {
|
||||
return mDuration;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Banner page 点击回调
|
||||
*/
|
||||
public interface BannerPageClickListener{
|
||||
void onPageClick(View view, int position);
|
||||
}
|
||||
|
||||
public static int dpToPx(int dp) {
|
||||
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.fengliyan.uikit.mzbanner.holder;
|
||||
|
||||
/**
|
||||
* Created by zhouwei on 17/5/26.
|
||||
*/
|
||||
|
||||
public interface MZHolderCreator<VH extends MZViewHolder> {
|
||||
/**
|
||||
* 创建ViewHolder
|
||||
* @return
|
||||
*/
|
||||
public VH createViewHolder();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.fengliyan.uikit.mzbanner.holder;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Created by zhouwei on 17/5/26.
|
||||
*/
|
||||
|
||||
public interface MZViewHolder<T> {
|
||||
/**
|
||||
* 创建View
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
View createView(Context context);
|
||||
|
||||
/**
|
||||
* 绑定数据
|
||||
* @param context
|
||||
* @param position
|
||||
* @param data
|
||||
*/
|
||||
void onBind(Context context, int position, T data);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.fengliyan.uikit.mzbanner.transformer;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
/**
|
||||
* Created by zhouwei on 17/8/20.
|
||||
*/
|
||||
|
||||
public class CoverModeTransformer implements ViewPager.PageTransformer {
|
||||
|
||||
private float reduceX = 0.0f;
|
||||
private float itemWidth = 0;
|
||||
private float offsetPosition = 0f;
|
||||
private int mCoverWidth;
|
||||
private float mScaleMax = 1.0f;
|
||||
private float mScaleMin = 0.9f;
|
||||
private ViewPager mViewPager;
|
||||
public CoverModeTransformer(ViewPager pager){
|
||||
mViewPager = pager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformPage(View view, float position) {
|
||||
if (offsetPosition == 0f) {
|
||||
float paddingLeft = mViewPager.getPaddingLeft();
|
||||
float paddingRight = mViewPager.getPaddingRight();
|
||||
float width = mViewPager.getMeasuredWidth();
|
||||
offsetPosition = paddingLeft / (width - paddingLeft - paddingRight);
|
||||
}
|
||||
float currentPos = position - offsetPosition;
|
||||
if (itemWidth == 0) {
|
||||
itemWidth = view.getWidth();
|
||||
//由于左右边的缩小而减小的x的大小的一半
|
||||
reduceX = (2.0f - mScaleMax - mScaleMin) * itemWidth / 2.0f;
|
||||
}
|
||||
if (currentPos <= -1.0f) {
|
||||
view.setTranslationX(reduceX + mCoverWidth);
|
||||
view.setScaleX(mScaleMin);
|
||||
view.setScaleY(mScaleMin);
|
||||
} else if (currentPos <= 1.0) {
|
||||
float scale = (mScaleMax - mScaleMin) * Math.abs(1.0f - Math.abs(currentPos));
|
||||
float translationX = currentPos * -reduceX;
|
||||
if (currentPos <= -0.5) {//两个view中间的临界,这时两个view在同一层,左侧View需要往X轴正方向移动覆盖的值()
|
||||
view.setTranslationX(translationX + mCoverWidth * Math.abs(Math.abs(currentPos) - 0.5f) / 0.5f);
|
||||
} else if (currentPos <= 0.0f) {
|
||||
view.setTranslationX(translationX);
|
||||
} else if (currentPos >= 0.5) {//两个view中间的临界,这时两个view在同一层
|
||||
view.setTranslationX(translationX - mCoverWidth * Math.abs(Math.abs(currentPos) - 0.5f) / 0.5f);
|
||||
} else {
|
||||
view.setTranslationX(translationX);
|
||||
}
|
||||
view.setScaleX(scale + mScaleMin);
|
||||
view.setScaleY(scale + mScaleMin);
|
||||
} else {
|
||||
view.setScaleX(mScaleMin);
|
||||
view.setScaleY(mScaleMin);
|
||||
view.setTranslationX(-reduceX - mCoverWidth);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.fengliyan.uikit.mzbanner.transformer;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
/**
|
||||
* Created by zhouwei on 17/5/26.
|
||||
*/
|
||||
|
||||
public class ScaleYTransformer implements ViewPager.PageTransformer {
|
||||
private static final float MIN_SCALE = 0.9F;
|
||||
@Override
|
||||
public void transformPage(View page, float position) {
|
||||
|
||||
if(position < -1){
|
||||
page.setScaleY(MIN_SCALE);
|
||||
}else if(position<= 1){
|
||||
//
|
||||
float scale = Math.max(MIN_SCALE,1 - Math.abs(position));
|
||||
page.setScaleY(scale);
|
||||
/*page.setScaleX(scale);
|
||||
|
||||
if(position<0){
|
||||
page.setTranslationX(width * (1 - scale) /2);
|
||||
}else{
|
||||
page.setTranslationX(-width * (1 - scale) /2);
|
||||
}*/
|
||||
|
||||
}else{
|
||||
page.setScaleY(MIN_SCALE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.fengliyan.uikit.photopicker;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.fengliyan.base.base.permission.AbsPermissionResultCallBack;
|
||||
import com.fengliyan.base.base.permission.PermissionHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 图片选择器
|
||||
* Created by nereo on 16/3/17.
|
||||
*/
|
||||
public class MultiImageSelector {
|
||||
|
||||
public static final String EXTRA_RESULT = MultiImageSelectorActivity.EXTRA_RESULT;
|
||||
|
||||
private boolean mShowCamera = true;
|
||||
private int mMaxCount = 9;
|
||||
private int mMode = MultiImageSelectorActivity.MODE_MULTI;
|
||||
private ArrayList<String> mOriginData;
|
||||
private static MultiImageSelector sSelector;
|
||||
|
||||
@Deprecated
|
||||
private MultiImageSelector(Context context) {
|
||||
|
||||
}
|
||||
|
||||
private MultiImageSelector() {
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static MultiImageSelector create(Context context) {
|
||||
if (sSelector == null) {
|
||||
sSelector = new MultiImageSelector(context);
|
||||
}
|
||||
return sSelector;
|
||||
}
|
||||
|
||||
public static MultiImageSelector create() {
|
||||
if (sSelector == null) {
|
||||
sSelector = new MultiImageSelector();
|
||||
}
|
||||
return sSelector;
|
||||
}
|
||||
|
||||
public MultiImageSelector showCamera(boolean show) {
|
||||
mShowCamera = show;
|
||||
return sSelector;
|
||||
}
|
||||
|
||||
public MultiImageSelector count(int count) {
|
||||
mMaxCount = count;
|
||||
return sSelector;
|
||||
}
|
||||
|
||||
public MultiImageSelector single() {
|
||||
mMode = MultiImageSelectorActivity.MODE_SINGLE;
|
||||
return sSelector;
|
||||
}
|
||||
|
||||
public MultiImageSelector multi() {
|
||||
mMode = MultiImageSelectorActivity.MODE_MULTI;
|
||||
return sSelector;
|
||||
}
|
||||
|
||||
public MultiImageSelector origin(ArrayList<String> images) {
|
||||
mOriginData = images;
|
||||
return sSelector;
|
||||
}
|
||||
|
||||
public void start(final Activity activity, final int requestCode) {
|
||||
final Context context = activity;
|
||||
PermissionHelper.request(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
new AbsPermissionResultCallBack() {
|
||||
@Override
|
||||
public void onPermissionGranted() {
|
||||
activity.startActivityForResult(createIntent(context), requestCode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void start(final Fragment fragment, final int requestCode) {
|
||||
final Context context = fragment.getContext();
|
||||
PermissionHelper.request(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
new AbsPermissionResultCallBack() {
|
||||
@Override
|
||||
public void onPermissionGranted() {
|
||||
fragment.startActivityForResult(createIntent(context), requestCode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean hasPermission(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
// Permission was added in API Level 16
|
||||
return ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Intent createIntent(Context context) {
|
||||
Intent intent = new Intent(context, MultiImageSelectorActivity.class);
|
||||
intent.putExtra(MultiImageSelectorActivity.EXTRA_SHOW_CAMERA, mShowCamera);
|
||||
intent.putExtra(MultiImageSelectorActivity.EXTRA_SELECT_COUNT, mMaxCount);
|
||||
if (mOriginData != null) {
|
||||
intent.putStringArrayListExtra(MultiImageSelectorActivity.EXTRA_DEFAULT_SELECTED_LIST, mOriginData);
|
||||
}
|
||||
intent.putExtra(MultiImageSelectorActivity.EXTRA_SELECT_MODE, mMode);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package com.fengliyan.uikit.photopicker;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Multi image selector
|
||||
* Created by Nereo on 2015/4/7.
|
||||
* Updated by nereo on 2016/1/19.
|
||||
* Updated by nereo on 2016/5/18.
|
||||
*/
|
||||
public class MultiImageSelectorActivity extends AppCompatActivity
|
||||
implements MultiImageSelectorFragment.Callback{
|
||||
|
||||
// Single choice
|
||||
public static final int MODE_SINGLE = 0;
|
||||
// Multi choice
|
||||
public static final int MODE_MULTI = 1;
|
||||
|
||||
/** Max image size,int,{@link #DEFAULT_IMAGE_SIZE} by default */
|
||||
public static final String EXTRA_SELECT_COUNT = "max_select_count";
|
||||
/** Select mode,{@link #MODE_MULTI} by default */
|
||||
public static final String EXTRA_SELECT_MODE = "select_count_mode";
|
||||
/** Whether show camera,true by default */
|
||||
public static final String EXTRA_SHOW_CAMERA = "show_camera";
|
||||
/** Result data set,ArrayList<String>*/
|
||||
public static final String EXTRA_RESULT = "select_result";
|
||||
/** Original data set */
|
||||
public static final String EXTRA_DEFAULT_SELECTED_LIST = "default_list";
|
||||
// Default image size
|
||||
private static final int DEFAULT_IMAGE_SIZE = 9;
|
||||
|
||||
private ArrayList<String> resultList = new ArrayList<>();
|
||||
private Button mSubmitButton;
|
||||
private int mDefaultCount = DEFAULT_IMAGE_SIZE;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setTheme(R.style.MIS_NO_ACTIONBAR);
|
||||
setContentView(R.layout.mis_activity_default);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().setStatusBarColor(Color.BLACK);
|
||||
}
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
if(toolbar != null){
|
||||
setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
final ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
final Intent intent = getIntent();
|
||||
mDefaultCount = intent.getIntExtra(EXTRA_SELECT_COUNT, DEFAULT_IMAGE_SIZE);
|
||||
final int mode = intent.getIntExtra(EXTRA_SELECT_MODE, MODE_MULTI);
|
||||
final boolean isShow = intent.getBooleanExtra(EXTRA_SHOW_CAMERA, true);
|
||||
if(mode == MODE_MULTI && intent.hasExtra(EXTRA_DEFAULT_SELECTED_LIST)) {
|
||||
resultList = intent.getStringArrayListExtra(EXTRA_DEFAULT_SELECTED_LIST);
|
||||
}
|
||||
|
||||
mSubmitButton = (Button) findViewById(R.id.commit);
|
||||
if(mode == MODE_MULTI){
|
||||
updateDoneText(resultList);
|
||||
mSubmitButton.setVisibility(View.VISIBLE);
|
||||
mSubmitButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if(resultList != null && resultList.size() >0){
|
||||
// Notify success
|
||||
Intent data = new Intent();
|
||||
data.putStringArrayListExtra(EXTRA_RESULT, resultList);
|
||||
setResult(RESULT_OK, data);
|
||||
}else{
|
||||
setResult(RESULT_CANCELED);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}else{
|
||||
mSubmitButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if(savedInstanceState == null){
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(MultiImageSelectorFragment.EXTRA_SELECT_COUNT, mDefaultCount);
|
||||
bundle.putInt(MultiImageSelectorFragment.EXTRA_SELECT_MODE, mode);
|
||||
bundle.putBoolean(MultiImageSelectorFragment.EXTRA_SHOW_CAMERA, isShow);
|
||||
bundle.putStringArrayList(MultiImageSelectorFragment.EXTRA_DEFAULT_SELECTED_LIST, resultList);
|
||||
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.add(R.id.image_grid, Fragment.instantiate(this, MultiImageSelectorFragment.class.getName(), bundle))
|
||||
.commit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update done button by select image data
|
||||
* @param resultList selected image data
|
||||
*/
|
||||
private void updateDoneText(ArrayList<String> resultList){
|
||||
int size = 0;
|
||||
if(resultList == null || resultList.size()<=0){
|
||||
mSubmitButton.setText(R.string.mis_action_done);
|
||||
mSubmitButton.setEnabled(false);
|
||||
}else{
|
||||
size = resultList.size();
|
||||
mSubmitButton.setEnabled(true);
|
||||
}
|
||||
mSubmitButton.setText(getString(R.string.mis_action_button_string,
|
||||
getString(R.string.mis_action_done), size, mDefaultCount));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSingleImageSelected(String path) {
|
||||
Intent data = new Intent();
|
||||
resultList.add(path);
|
||||
data.putStringArrayListExtra(EXTRA_RESULT, resultList);
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageSelected(String path) {
|
||||
if(!resultList.contains(path)) {
|
||||
resultList.add(path);
|
||||
}
|
||||
updateDoneText(resultList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageUnselected(String path) {
|
||||
if(resultList.contains(path)){
|
||||
resultList.remove(path);
|
||||
}
|
||||
updateDoneText(resultList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraShot(File imageFile) {
|
||||
if(imageFile != null) {
|
||||
// notify system the image has change
|
||||
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(imageFile)));
|
||||
|
||||
Intent data = new Intent();
|
||||
resultList.add(imageFile.getAbsolutePath());
|
||||
data.putStringArrayListExtra(EXTRA_RESULT, resultList);
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,576 @@
|
||||
package com.fengliyan.uikit.photopicker;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ListPopupWindow;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.CursorLoader;
|
||||
import androidx.loader.content.Loader;
|
||||
|
||||
import com.fengliyan.base.base.permission.PermissionDialog;
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.photopicker.adapter.FolderAdapter;
|
||||
import com.fengliyan.uikit.photopicker.adapter.ImageGridAdapter;
|
||||
import com.fengliyan.uikit.photopicker.bean.Folder;
|
||||
import com.fengliyan.uikit.photopicker.bean.Image;
|
||||
import com.fengliyan.uikit.photopicker.utils.FileUtils;
|
||||
import com.fengliyan.uikit.photopicker.utils.ScreenUtils;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Multi image selector Fragment
|
||||
* Created by Nereo on 2015/4/7.
|
||||
* Updated by nereo on 2016/5/18.
|
||||
*/
|
||||
public class MultiImageSelectorFragment extends Fragment {
|
||||
|
||||
public static final String TAG = "MultiImageSelectorFragment";
|
||||
|
||||
private static final int REQUEST_STORAGE_WRITE_ACCESS_PERMISSION = 110;
|
||||
private static final int REQUEST_CAMERA = 100;
|
||||
|
||||
private static final String KEY_TEMP_FILE = "key_temp_file";
|
||||
|
||||
// Single choice
|
||||
public static final int MODE_SINGLE = 0;
|
||||
// Multi choice
|
||||
public static final int MODE_MULTI = 1;
|
||||
|
||||
/**
|
||||
* Max image size,int,
|
||||
*/
|
||||
public static final String EXTRA_SELECT_COUNT = "max_select_count";
|
||||
/**
|
||||
* Select mode,{@link #MODE_MULTI} by default
|
||||
*/
|
||||
public static final String EXTRA_SELECT_MODE = "select_count_mode";
|
||||
/**
|
||||
* Whether show camera,true by default
|
||||
*/
|
||||
public static final String EXTRA_SHOW_CAMERA = "show_camera";
|
||||
/**
|
||||
* Original data set
|
||||
*/
|
||||
public static final String EXTRA_DEFAULT_SELECTED_LIST = "default_list";
|
||||
|
||||
// loaders
|
||||
private static final int LOADER_ALL = 0;
|
||||
private static final int LOADER_CATEGORY = 1;
|
||||
|
||||
// image result data set
|
||||
private ArrayList<String> resultList = new ArrayList<>();
|
||||
// folder result data set
|
||||
private ArrayList<Folder> mResultFolder = new ArrayList<>();
|
||||
|
||||
private GridView mGridView;
|
||||
private Callback mCallback;
|
||||
|
||||
private ImageGridAdapter mImageAdapter;
|
||||
private FolderAdapter mFolderAdapter;
|
||||
|
||||
private ListPopupWindow mFolderPopupWindow;
|
||||
|
||||
private TextView mCategoryText;
|
||||
private View mPopupAnchorView;
|
||||
|
||||
private boolean hasFolderGened = false;
|
||||
|
||||
private File mTmpFile;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
try {
|
||||
mCallback = (Callback) getActivity();
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException("The Activity must implement MultiImageSelectorFragment.Callback interface...");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.mis_fragment_multi_image, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
final int mode = selectMode();
|
||||
if (mode == MODE_MULTI) {
|
||||
ArrayList<String> tmp = getArguments().getStringArrayList(EXTRA_DEFAULT_SELECTED_LIST);
|
||||
if (tmp != null && tmp.size() > 0) {
|
||||
resultList = tmp;
|
||||
}
|
||||
}
|
||||
mImageAdapter = new ImageGridAdapter(getActivity(), showCamera(), 3);
|
||||
mImageAdapter.showSelectIndicator(mode == MODE_MULTI);
|
||||
|
||||
mPopupAnchorView = view.findViewById(R.id.footer);
|
||||
|
||||
mCategoryText = (TextView) view.findViewById(R.id.category_btn);
|
||||
mCategoryText.setText(R.string.mis_folder_all);
|
||||
mCategoryText.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
||||
if (mFolderPopupWindow == null) {
|
||||
createPopupFolderList();
|
||||
}
|
||||
|
||||
if (mFolderPopupWindow.isShowing()) {
|
||||
mFolderPopupWindow.dismiss();
|
||||
} else {
|
||||
mFolderPopupWindow.show();
|
||||
int index = mFolderAdapter.getSelectIndex();
|
||||
index = index == 0 ? index : index - 1;
|
||||
mFolderPopupWindow.getListView().setSelection(index);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mGridView = (GridView) view.findViewById(R.id.grid);
|
||||
mGridView.setAdapter(mImageAdapter);
|
||||
mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
if (mImageAdapter.isShowCamera()) {
|
||||
if (i == 0) {
|
||||
showCameraAction();
|
||||
} else {
|
||||
Image image = (Image) adapterView.getAdapter().getItem(i);
|
||||
selectImageFromGrid(image, mode);
|
||||
}
|
||||
} else {
|
||||
Image image = (Image) adapterView.getAdapter().getItem(i);
|
||||
selectImageFromGrid(image, mode);
|
||||
}
|
||||
}
|
||||
});
|
||||
mGridView.setOnScrollListener(new AbsListView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
||||
if (scrollState == SCROLL_STATE_FLING) {
|
||||
Picasso.with(view.getContext()).pauseTag(TAG);
|
||||
} else {
|
||||
Picasso.with(view.getContext()).resumeTag(TAG);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
mFolderAdapter = new FolderAdapter(getActivity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create popup ListView
|
||||
*/
|
||||
private void createPopupFolderList() {
|
||||
Point point = ScreenUtils.getScreenSize(getActivity());
|
||||
int width = point.x;
|
||||
int height = (int) (point.y * (4.5f / 8.0f));
|
||||
mFolderPopupWindow = new ListPopupWindow(getActivity());
|
||||
mFolderPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.WHITE));
|
||||
mFolderPopupWindow.setAdapter(mFolderAdapter);
|
||||
mFolderPopupWindow.setContentWidth(width);
|
||||
mFolderPopupWindow.setWidth(width);
|
||||
mFolderPopupWindow.setHeight(height);
|
||||
mFolderPopupWindow.setAnchorView(mPopupAnchorView);
|
||||
mFolderPopupWindow.setModal(true);
|
||||
mFolderPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
|
||||
mFolderAdapter.setSelectIndex(i);
|
||||
|
||||
final int index = i;
|
||||
final AdapterView v = adapterView;
|
||||
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mFolderPopupWindow.dismiss();
|
||||
|
||||
if (index == 0) {
|
||||
getActivity().getSupportLoaderManager().restartLoader(LOADER_ALL, null, mLoaderCallback);
|
||||
mCategoryText.setText(R.string.mis_folder_all);
|
||||
if (showCamera()) {
|
||||
mImageAdapter.setShowCamera(true);
|
||||
} else {
|
||||
mImageAdapter.setShowCamera(false);
|
||||
}
|
||||
} else {
|
||||
Folder folder = (Folder) v.getAdapter().getItem(index);
|
||||
if (null != folder) {
|
||||
mImageAdapter.setData(folder.images);
|
||||
mCategoryText.setText(folder.name);
|
||||
if (resultList != null && resultList.size() > 0) {
|
||||
mImageAdapter.setDefaultSelected(resultList);
|
||||
}
|
||||
}
|
||||
mImageAdapter.setShowCamera(false);
|
||||
}
|
||||
|
||||
mGridView.smoothScrollToPosition(0);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putSerializable(KEY_TEMP_FILE, mTmpFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
|
||||
super.onViewStateRestored(savedInstanceState);
|
||||
if (savedInstanceState != null) {
|
||||
mTmpFile = (File) savedInstanceState.getSerializable(KEY_TEMP_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
// load image data
|
||||
getActivity().getSupportLoaderManager().initLoader(LOADER_ALL, null, mLoaderCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_CAMERA) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (mTmpFile != null) {
|
||||
if (mCallback != null) {
|
||||
mCallback.onCameraShot(mTmpFile);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// delete tmp file
|
||||
while (mTmpFile != null && mTmpFile.exists()) {
|
||||
boolean success = mTmpFile.delete();
|
||||
if (success) {
|
||||
mTmpFile = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
if (mFolderPopupWindow != null) {
|
||||
if (mFolderPopupWindow.isShowing()) {
|
||||
mFolderPopupWindow.dismiss();
|
||||
}
|
||||
}
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open camera
|
||||
*/
|
||||
private void showCameraAction() {
|
||||
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
getString(R.string.mis_permission_rationale_write_storage),
|
||||
REQUEST_STORAGE_WRITE_ACCESS_PERMISSION);
|
||||
} else if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermission(Manifest.permission.CAMERA,
|
||||
getString(R.string.mis_permission_rationale_camera),
|
||||
REQUEST_CAMERA);
|
||||
} else {
|
||||
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
|
||||
try {
|
||||
mTmpFile = FileUtils.createTmpFile(getActivity());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (mTmpFile != null && mTmpFile.exists()) {
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, getImageContentUri(getActivity(), mTmpFile));
|
||||
startActivityForResult(intent, REQUEST_CAMERA);
|
||||
} else {
|
||||
Toast.makeText(getActivity(), R.string.mis_error_image_not_exist, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(getActivity(), R.string.mis_msg_no_camera, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Uri getImageContentUri(Context context, File imageFile) {
|
||||
String filePath = imageFile.getAbsolutePath();
|
||||
Cursor cursor = context.getContentResolver().query(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
new String[]{MediaStore.Images.Media._ID},
|
||||
MediaStore.Images.Media.DATA + "=? ",
|
||||
new String[]{filePath}, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int id = cursor.getInt(cursor
|
||||
.getColumnIndex(MediaStore.MediaColumns._ID));
|
||||
Uri baseUri = Uri.parse("content://media/external/images/media");
|
||||
return Uri.withAppendedPath(baseUri, "" + id);
|
||||
} else {
|
||||
if (imageFile.exists()) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(MediaStore.Images.Media.DATA, filePath);
|
||||
return context.getContentResolver().insert(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PermissionDialog permissionDialog;
|
||||
|
||||
private void requestPermission(final String permission, String rationale, final int requestCode) {
|
||||
permissionDialog = new PermissionDialog(getContext(), new String[]{permission});
|
||||
permissionDialog.show();
|
||||
if (shouldShowRequestPermissionRationale(permission)) {
|
||||
new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.mis_permission_dialog_title)
|
||||
.setMessage(rationale)
|
||||
.setPositiveButton(R.string.mis_permission_dialog_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
requestPermissions(new String[]{permission}, requestCode);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.mis_permission_dialog_cancel, null)
|
||||
.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
if (permissionDialog != null && permissionDialog.isShowing()) {
|
||||
permissionDialog.dismiss();
|
||||
}
|
||||
}
|
||||
})
|
||||
.create().show();
|
||||
} else {
|
||||
requestPermissions(new String[]{permission}, requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
if (requestCode == REQUEST_STORAGE_WRITE_ACCESS_PERMISSION) {
|
||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
showCameraAction();
|
||||
}
|
||||
} else {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
if (permissionDialog != null && permissionDialog.isShowing()) {
|
||||
permissionDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* notify callback
|
||||
*
|
||||
* @param image image data
|
||||
*/
|
||||
private void selectImageFromGrid(Image image, int mode) {
|
||||
if (image != null) {
|
||||
if (mode == MODE_MULTI) {
|
||||
if (resultList.contains(image.path)) {
|
||||
resultList.remove(image.path);
|
||||
if (mCallback != null) {
|
||||
mCallback.onImageUnselected(image.path);
|
||||
}
|
||||
} else {
|
||||
if (selectImageCount() == resultList.size()) {
|
||||
Toast.makeText(getActivity(), R.string.mis_msg_amount_limit, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
resultList.add(image.path);
|
||||
if (mCallback != null) {
|
||||
mCallback.onImageSelected(image.path);
|
||||
}
|
||||
}
|
||||
mImageAdapter.select(image);
|
||||
} else if (mode == MODE_SINGLE) {
|
||||
if (mCallback != null) {
|
||||
mCallback.onSingleImageSelected(image.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private LoaderManager.LoaderCallbacks<Cursor> mLoaderCallback = new LoaderManager.LoaderCallbacks<Cursor>() {
|
||||
|
||||
private final String[] IMAGE_PROJECTION = {
|
||||
MediaStore.Images.Media.DATA,
|
||||
MediaStore.Images.Media.DISPLAY_NAME,
|
||||
MediaStore.Images.Media.DATE_ADDED,
|
||||
MediaStore.Images.Media.MIME_TYPE,
|
||||
MediaStore.Images.Media.SIZE,
|
||||
MediaStore.Images.Media._ID};
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
CursorLoader cursorLoader = null;
|
||||
if (id == LOADER_ALL) {
|
||||
cursorLoader = new CursorLoader(getActivity(),
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_PROJECTION,
|
||||
IMAGE_PROJECTION[4] + ">0 AND " + IMAGE_PROJECTION[3] + "=? OR " + IMAGE_PROJECTION[3] + "=? ",
|
||||
new String[]{"image/jpeg", "image/png"}, IMAGE_PROJECTION[2] + " DESC");
|
||||
} else if (id == LOADER_CATEGORY) {
|
||||
cursorLoader = new CursorLoader(getActivity(),
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_PROJECTION,
|
||||
IMAGE_PROJECTION[4] + ">0 AND " + IMAGE_PROJECTION[0] + " like '%" + args.getString("path") + "%'",
|
||||
null, IMAGE_PROJECTION[2] + " DESC");
|
||||
}
|
||||
return cursorLoader;
|
||||
}
|
||||
|
||||
private boolean fileExist(String path) {
|
||||
if (!TextUtils.isEmpty(path)) {
|
||||
return new File(path).exists();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||
if (data != null) {
|
||||
if (data.getCount() > 0) {
|
||||
List<Image> images = new ArrayList<>();
|
||||
data.moveToFirst();
|
||||
do {
|
||||
String path = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[0]));
|
||||
String name = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[1]));
|
||||
long dateTime = data.getLong(data.getColumnIndexOrThrow(IMAGE_PROJECTION[2]));
|
||||
if (!fileExist(path)) {
|
||||
continue;
|
||||
}
|
||||
Image image = null;
|
||||
if (!TextUtils.isEmpty(name)) {
|
||||
image = new Image(path, name, dateTime);
|
||||
images.add(image);
|
||||
}
|
||||
if (!hasFolderGened) {
|
||||
// get all folder data
|
||||
File folderFile = new File(path).getParentFile();
|
||||
if (folderFile != null && folderFile.exists()) {
|
||||
String fp = folderFile.getAbsolutePath();
|
||||
Folder f = getFolderByPath(fp);
|
||||
if (f == null) {
|
||||
Folder folder = new Folder();
|
||||
folder.name = folderFile.getName();
|
||||
folder.path = fp;
|
||||
folder.cover = image;
|
||||
List<Image> imageList = new ArrayList<>();
|
||||
imageList.add(image);
|
||||
folder.images = imageList;
|
||||
mResultFolder.add(folder);
|
||||
} else {
|
||||
f.images.add(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} while (data.moveToNext());
|
||||
|
||||
mImageAdapter.setData(images);
|
||||
if (resultList != null && resultList.size() > 0) {
|
||||
mImageAdapter.setDefaultSelected(resultList);
|
||||
}
|
||||
if (!hasFolderGened) {
|
||||
mFolderAdapter.setData(mResultFolder);
|
||||
hasFolderGened = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
private Folder getFolderByPath(String path) {
|
||||
if (mResultFolder != null) {
|
||||
for (Folder folder : mResultFolder) {
|
||||
if (TextUtils.equals(folder.path, path)) {
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean showCamera() {
|
||||
return getArguments() == null || getArguments().getBoolean(EXTRA_SHOW_CAMERA, true);
|
||||
}
|
||||
|
||||
private int selectMode() {
|
||||
return getArguments() == null ? MODE_MULTI : getArguments().getInt(EXTRA_SELECT_MODE);
|
||||
}
|
||||
|
||||
private int selectImageCount() {
|
||||
return getArguments() == null ? 9 : getArguments().getInt(EXTRA_SELECT_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for host activity
|
||||
*/
|
||||
public interface Callback {
|
||||
void onSingleImageSelected(String path);
|
||||
|
||||
void onImageSelected(String path);
|
||||
|
||||
void onImageUnselected(String path);
|
||||
|
||||
void onCameraShot(File imageFile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package com.fengliyan.uikit.photopicker.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.photopicker.bean.Folder;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件夹Adapter
|
||||
* Created by Nereo on 2015/4/7.
|
||||
* Updated by nereo on 2016/1/19.
|
||||
*/
|
||||
public class FolderAdapter extends BaseAdapter {
|
||||
|
||||
private Context mContext;
|
||||
private LayoutInflater mInflater;
|
||||
|
||||
private List<Folder> mFolders = new ArrayList<>();
|
||||
|
||||
int mImageSize;
|
||||
|
||||
int lastSelected = 0;
|
||||
|
||||
public FolderAdapter(Context context) {
|
||||
mContext = context;
|
||||
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mImageSize = mContext.getResources().getDimensionPixelOffset(R.dimen.mis_folder_cover_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据集
|
||||
*
|
||||
* @param folders
|
||||
*/
|
||||
public void setData(List<Folder> folders) {
|
||||
if (folders != null && folders.size() > 0) {
|
||||
mFolders = folders;
|
||||
} else {
|
||||
mFolders.clear();
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mFolders.size() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Folder getItem(int i) {
|
||||
if (i == 0) return null;
|
||||
return mFolders.get(i - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||
ViewHolder holder;
|
||||
if (view == null) {
|
||||
view = mInflater.inflate(R.layout.mis_list_item_folder, viewGroup, false);
|
||||
holder = new ViewHolder(view);
|
||||
} else {
|
||||
holder = (ViewHolder) view.getTag();
|
||||
}
|
||||
if (holder != null) {
|
||||
if (i == 0) {
|
||||
holder.name.setText(R.string.mis_folder_all);
|
||||
holder.path.setText("/sdcard");
|
||||
holder.size.setText(String.format("%d%s",
|
||||
getTotalImageSize(), mContext.getResources().getString(R.string.mis_photo_unit)));
|
||||
if (mFolders.size() > 0) {
|
||||
Folder f = mFolders.get(0);
|
||||
if (f != null) {
|
||||
// RequestOptions requestOptions = new RequestOptions()
|
||||
// .centerCrop()
|
||||
// .placeholder(R.drawable.mis_default_error)
|
||||
// .error(R.drawable.mis_default_error)
|
||||
// .override(R.dimen.mis_folder_cover_size, R.dimen.mis_folder_cover_size);
|
||||
// Glide.with(mContext).asBitmap()
|
||||
// .load(new File(f.cover.path))
|
||||
// .apply(requestOptions)
|
||||
// .into(holder.cover);
|
||||
Picasso.with(mContext)
|
||||
.load(new File(f.cover.path))
|
||||
.error(R.drawable.mis_default_error)
|
||||
.resizeDimen(R.dimen.mis_folder_cover_size, R.dimen.mis_folder_cover_size)
|
||||
.centerCrop()
|
||||
.into(holder.cover);
|
||||
} else {
|
||||
holder.cover.setImageResource(R.drawable.mis_default_error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
holder.bindData(getItem(i));
|
||||
}
|
||||
if (lastSelected == i) {
|
||||
holder.indicator.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.indicator.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
private int getTotalImageSize() {
|
||||
int result = 0;
|
||||
if (mFolders != null && mFolders.size() > 0) {
|
||||
for (Folder f : mFolders) {
|
||||
result += f.images.size();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setSelectIndex(int i) {
|
||||
if (lastSelected == i) return;
|
||||
|
||||
lastSelected = i;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public int getSelectIndex() {
|
||||
return lastSelected;
|
||||
}
|
||||
|
||||
class ViewHolder {
|
||||
ImageView cover;
|
||||
TextView name;
|
||||
TextView path;
|
||||
TextView size;
|
||||
ImageView indicator;
|
||||
|
||||
ViewHolder(View view) {
|
||||
cover = (ImageView) view.findViewById(R.id.cover);
|
||||
name = (TextView) view.findViewById(R.id.name);
|
||||
path = (TextView) view.findViewById(R.id.path);
|
||||
size = (TextView) view.findViewById(R.id.size);
|
||||
indicator = (ImageView) view.findViewById(R.id.indicator);
|
||||
view.setTag(this);
|
||||
}
|
||||
|
||||
void bindData(Folder data) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
name.setText(data.name);
|
||||
path.setText(data.path);
|
||||
if (data.images != null) {
|
||||
size.setText(String.format("%d%s", data.images.size(), mContext.getResources().getString(R.string.mis_photo_unit)));
|
||||
} else {
|
||||
size.setText("*" + mContext.getResources().getString(R.string.mis_photo_unit));
|
||||
}
|
||||
if (data.cover != null) {
|
||||
// 显示图片
|
||||
// RequestOptions requestOptions = new RequestOptions()
|
||||
// .centerCrop()
|
||||
// .placeholder(R.drawable.mis_default_error)
|
||||
// .error(R.drawable.mis_default_error)
|
||||
// .override(R.dimen.mis_folder_cover_size, R.dimen.mis_folder_cover_size);
|
||||
// Glide.with(mContext).asBitmap()
|
||||
// .load(new File(data.cover.path))
|
||||
// .apply(requestOptions)
|
||||
// .into(cover);
|
||||
|
||||
Picasso.with(mContext)
|
||||
.load(new File(data.cover.path))
|
||||
.placeholder(R.drawable.mis_default_error)
|
||||
.resizeDimen(R.dimen.mis_folder_cover_size, R.dimen.mis_folder_cover_size)
|
||||
.centerCrop()
|
||||
.into(cover);
|
||||
} else {
|
||||
cover.setImageResource(R.drawable.mis_default_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
package com.fengliyan.uikit.photopicker.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.os.Build;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.photopicker.bean.Image;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
* 图片Adapter
|
||||
* Created by Nereo on 2015/4/7.
|
||||
* Updated by nereo on 2016/1/19.
|
||||
*/
|
||||
public class ImageGridAdapter extends BaseAdapter {
|
||||
|
||||
private static final int TYPE_CAMERA = 0;
|
||||
private static final int TYPE_NORMAL = 1;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private LayoutInflater mInflater;
|
||||
private boolean showCamera = true;
|
||||
private boolean showSelectIndicator = true;
|
||||
|
||||
private List<Image> mImages = new ArrayList<>();
|
||||
private List<Image> mSelectedImages = new ArrayList<>();
|
||||
|
||||
final int mGridWidth;
|
||||
|
||||
public ImageGridAdapter(Context context, boolean showCamera, int column){
|
||||
mContext = context;
|
||||
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
this.showCamera = showCamera;
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
int width = 0;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
|
||||
Point size = new Point();
|
||||
wm.getDefaultDisplay().getSize(size);
|
||||
width = size.x;
|
||||
}else{
|
||||
width = wm.getDefaultDisplay().getWidth();
|
||||
}
|
||||
mGridWidth = width / column;
|
||||
}
|
||||
/**
|
||||
* 显示选择指示器
|
||||
* @param b
|
||||
*/
|
||||
public void showSelectIndicator(boolean b) {
|
||||
showSelectIndicator = b;
|
||||
}
|
||||
|
||||
public void setShowCamera(boolean b){
|
||||
if(showCamera == b) return;
|
||||
|
||||
showCamera = b;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public boolean isShowCamera(){
|
||||
return showCamera;
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择某个图片,改变选择状态
|
||||
* @param image
|
||||
*/
|
||||
public void select(Image image) {
|
||||
if(mSelectedImages.contains(image)){
|
||||
mSelectedImages.remove(image);
|
||||
}else{
|
||||
mSelectedImages.add(image);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过图片路径设置默认选择
|
||||
* @param resultList
|
||||
*/
|
||||
public void setDefaultSelected(ArrayList<String> resultList) {
|
||||
for(String path : resultList){
|
||||
Image image = getImageByPath(path);
|
||||
if(image != null){
|
||||
mSelectedImages.add(image);
|
||||
}
|
||||
}
|
||||
if(mSelectedImages.size() > 0){
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private Image getImageByPath(String path){
|
||||
if(mImages != null && mImages.size()>0){
|
||||
for(Image image : mImages){
|
||||
if(image.path.equalsIgnoreCase(path)){
|
||||
return image;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置数据集
|
||||
* @param images
|
||||
*/
|
||||
public void setData(List<Image> images) {
|
||||
mSelectedImages.clear();
|
||||
|
||||
if(images != null && images.size()>0){
|
||||
mImages = images;
|
||||
}else{
|
||||
mImages.clear();
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if(showCamera){
|
||||
return position==0 ? TYPE_CAMERA : TYPE_NORMAL;
|
||||
}
|
||||
return TYPE_NORMAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return showCamera ? mImages.size()+1 : mImages.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getItem(int i) {
|
||||
if(showCamera){
|
||||
if(i == 0){
|
||||
return null;
|
||||
}
|
||||
return mImages.get(i-1);
|
||||
}else{
|
||||
return mImages.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||
|
||||
if(isShowCamera()){
|
||||
if(i == 0){
|
||||
view = mInflater.inflate(R.layout.mis_list_item_camera, viewGroup, false);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
ViewHolder holder;
|
||||
if(view == null){
|
||||
view = mInflater.inflate(R.layout.mis_list_item_image, viewGroup, false);
|
||||
holder = new ViewHolder(view);
|
||||
}else{
|
||||
holder = (ViewHolder) view.getTag();
|
||||
}
|
||||
|
||||
if(holder != null) {
|
||||
holder.bindData(getItem(i));
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
class ViewHolder {
|
||||
ImageView image;
|
||||
ImageView indicator;
|
||||
View mask;
|
||||
|
||||
ViewHolder(View view){
|
||||
image = (ImageView) view.findViewById(R.id.image);
|
||||
indicator = (ImageView) view.findViewById(R.id.checkmark);
|
||||
mask = view.findViewById(R.id.mask);
|
||||
view.setTag(this);
|
||||
}
|
||||
|
||||
void bindData(final Image data){
|
||||
if(data == null) return;
|
||||
// 处理单选和多选状态
|
||||
if(showSelectIndicator){
|
||||
indicator.setVisibility(View.VISIBLE);
|
||||
if(mSelectedImages.contains(data)){
|
||||
// 设置选中状态
|
||||
indicator.setImageResource(R.drawable.mis_btn_selected);
|
||||
mask.setVisibility(View.VISIBLE);
|
||||
}else{
|
||||
// 未选择
|
||||
indicator.setImageResource(R.drawable.mis_btn_unselected);
|
||||
mask.setVisibility(View.GONE);
|
||||
}
|
||||
}else{
|
||||
indicator.setVisibility(View.GONE);
|
||||
}
|
||||
File imageFile = new File(data.path);
|
||||
if (imageFile.exists()) {
|
||||
// 显示图片
|
||||
RequestOptions requestOptions = new RequestOptions()
|
||||
.centerCrop()
|
||||
.placeholder(R.drawable.mis_default_error)
|
||||
.error(R.drawable.mis_default_error)
|
||||
.override(mGridWidth, mGridWidth);
|
||||
Glide.with(mContext ).asBitmap()
|
||||
.load(imageFile)
|
||||
.apply(requestOptions)
|
||||
.into(image);
|
||||
// Picasso.with(mContext)
|
||||
// .load(imageFile)
|
||||
// .placeholder(R.drawable.mis_default_error)
|
||||
// .tag(MultiImageSelectorFragment.TAG)
|
||||
// .resize(mGridWidth, mGridWidth)
|
||||
// .centerCrop()
|
||||
// .into(image);
|
||||
}else{
|
||||
image.setImageResource(R.drawable.mis_default_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.fengliyan.uikit.photopicker.bean;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件夹
|
||||
* Created by Nereo on 2015/4/7.
|
||||
*/
|
||||
public class Folder {
|
||||
public String name;
|
||||
public String path;
|
||||
public Image cover;
|
||||
public List<Image> images;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
try {
|
||||
Folder other = (Folder) o;
|
||||
return TextUtils.equals(other.path, path);
|
||||
}catch (ClassCastException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return super.equals(o);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.fengliyan.uikit.photopicker.bean;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* 图片实体
|
||||
* Created by Nereo on 2015/4/7.
|
||||
*/
|
||||
public class Image {
|
||||
public String path;
|
||||
public String name;
|
||||
public long time;
|
||||
|
||||
public Image(String path, String name, long time){
|
||||
this.path = path;
|
||||
this.name = name;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
try {
|
||||
Image other = (Image) o;
|
||||
return TextUtils.equals(this.path, other.path);
|
||||
}catch (ClassCastException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return super.equals(o);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.fengliyan.uikit.photopicker.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static android.os.Environment.MEDIA_MOUNTED;
|
||||
|
||||
/**
|
||||
* 文件操作类
|
||||
* Created by Nereo on 2015/4/8.
|
||||
*/
|
||||
public class FileUtils {
|
||||
|
||||
private static final String JPEG_FILE_PREFIX = "IMG_";
|
||||
private static final String JPEG_FILE_SUFFIX = ".jpg";
|
||||
|
||||
public static File createTmpFile(Context context) throws IOException {
|
||||
File dir = null;
|
||||
if(TextUtils.equals(Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED)) {
|
||||
dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
|
||||
if (!dir.exists()) {
|
||||
dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM + "/Camera");
|
||||
if (!dir.exists()) {
|
||||
dir = getCacheDirectory(context, true);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
dir = getCacheDirectory(context, true);
|
||||
}
|
||||
return File.createTempFile(JPEG_FILE_PREFIX, JPEG_FILE_SUFFIX, dir);
|
||||
}
|
||||
|
||||
|
||||
private static final String EXTERNAL_STORAGE_PERMISSION = "android.permission.WRITE_EXTERNAL_STORAGE";
|
||||
|
||||
/**
|
||||
* Returns application cache directory. Cache directory will be created on SD card
|
||||
* <i>("/Android/data/[app_package_name]/cache")</i> if card is mounted and app has appropriate permission. Else -
|
||||
* Android defines cache directory on device's file system.
|
||||
*
|
||||
* @param context Application context
|
||||
* @return Cache {@link File directory}.<br />
|
||||
* <b>NOTE:</b> Can be null in some unpredictable cases (if SD card is unmounted and
|
||||
* {@link Context#getCacheDir() Context.getCacheDir()} returns null).
|
||||
*/
|
||||
public static File getCacheDirectory(Context context) {
|
||||
return getCacheDirectory(context, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns application cache directory. Cache directory will be created on SD card
|
||||
* <i>("/Android/data/[app_package_name]/cache")</i> (if card is mounted and app has appropriate permission) or
|
||||
* on device's file system depending incoming parameters.
|
||||
*
|
||||
* @param context Application context
|
||||
* @param preferExternal Whether prefer external location for cache
|
||||
* @return Cache {@link File directory}.<br />
|
||||
* <b>NOTE:</b> Can be null in some unpredictable cases (if SD card is unmounted and
|
||||
* {@link Context#getCacheDir() Context.getCacheDir()} returns null).
|
||||
*/
|
||||
public static File getCacheDirectory(Context context, boolean preferExternal) {
|
||||
File appCacheDir = null;
|
||||
String externalStorageState;
|
||||
try {
|
||||
externalStorageState = Environment.getExternalStorageState();
|
||||
} catch (NullPointerException e) { // (sh)it happens (Issue #660)
|
||||
externalStorageState = "";
|
||||
} catch (IncompatibleClassChangeError e) { // (sh)it happens too (Issue #989)
|
||||
externalStorageState = "";
|
||||
}
|
||||
if (preferExternal && MEDIA_MOUNTED.equals(externalStorageState) && hasExternalStoragePermission(context)) {
|
||||
appCacheDir = getExternalCacheDir(context);
|
||||
}
|
||||
if (appCacheDir == null) {
|
||||
appCacheDir = context.getCacheDir();
|
||||
}
|
||||
if (appCacheDir == null) {
|
||||
String cacheDirPath = "/data/data/" + context.getPackageName() + "/cache/";
|
||||
appCacheDir = new File(cacheDirPath);
|
||||
}
|
||||
return appCacheDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be
|
||||
* created on SD card <i>("/Android/data/[app_package_name]/cache/uil-images")</i> if card is mounted and app has
|
||||
* appropriate permission. Else - Android defines cache directory on device's file system.
|
||||
*
|
||||
* @param context Application context
|
||||
* @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
|
||||
* @return Cache {@link File directory}
|
||||
*/
|
||||
public static File getIndividualCacheDirectory(Context context, String cacheDir) {
|
||||
File appCacheDir = getCacheDirectory(context);
|
||||
File individualCacheDir = new File(appCacheDir, cacheDir);
|
||||
if (!individualCacheDir.exists()) {
|
||||
if (!individualCacheDir.mkdir()) {
|
||||
individualCacheDir = appCacheDir;
|
||||
}
|
||||
}
|
||||
return individualCacheDir;
|
||||
}
|
||||
|
||||
private static File getExternalCacheDir(Context context) {
|
||||
File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data");
|
||||
File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache");
|
||||
if (!appCacheDir.exists()) {
|
||||
if (!appCacheDir.mkdirs()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
new File(appCacheDir, ".nomedia").createNewFile();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
return appCacheDir;
|
||||
}
|
||||
|
||||
private static boolean hasExternalStoragePermission(Context context) {
|
||||
int perm = context.checkCallingOrSelfPermission(EXTERNAL_STORAGE_PERMISSION);
|
||||
return perm == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.fengliyan.uikit.photopicker.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.os.Build;
|
||||
import android.view.Display;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/**
|
||||
* 屏幕工具
|
||||
* Created by nereo on 15/11/19.
|
||||
* Updated by nereo on 2016/1/19.
|
||||
*/
|
||||
public class ScreenUtils {
|
||||
|
||||
public static Point getScreenSize(Context context){
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
Display display = wm.getDefaultDisplay();
|
||||
Point out = new Point();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
|
||||
display.getSize(out);
|
||||
}else{
|
||||
int width = display.getWidth();
|
||||
int height = display.getHeight();
|
||||
out.set(width, height);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.fengliyan.uikit.photopicker.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 时间处理工具
|
||||
* Created by Nereo on 2015/4/8.
|
||||
*/
|
||||
public class TimeUtils {
|
||||
|
||||
public static String timeFormat(long timeMillis, String pattern){
|
||||
SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.CHINA);
|
||||
return format.format(new Date(timeMillis));
|
||||
}
|
||||
|
||||
public static String formatPhotoDate(long time){
|
||||
return timeFormat(time, "yyyy-MM-dd");
|
||||
}
|
||||
|
||||
public static String formatPhotoDate(String path){
|
||||
File file = new File(path);
|
||||
if(file.exists()){
|
||||
long time = file.lastModified();
|
||||
return formatPhotoDate(time);
|
||||
}
|
||||
return "1970-01-01";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.fengliyan.uikit.photopicker.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
* Created by nereo on 15/11/10.
|
||||
*/
|
||||
public class SquareFrameLayout extends FrameLayout {
|
||||
public SquareFrameLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SquareFrameLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.fengliyan.uikit.photopicker.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/** An image view which always remains square with respect to its width. */
|
||||
public class SquaredImageView extends ImageView {
|
||||
public SquaredImageView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SquaredImageView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.fengliyan.uikit.picker.citypicker;
|
||||
|
||||
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import com.fengliyan.uikit.picker.citypicker.adapter.OnPickListener;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.HotCity;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.LocateState;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.LocatedCity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: Bro0cL
|
||||
* @Date: 2018/2/6 17:52
|
||||
*/
|
||||
public class CityPicker {
|
||||
private static final String TAG = "CityPicker";
|
||||
|
||||
private static CityPicker mInstance;
|
||||
private CityPickerDialogFragment mCityPickerFragment;
|
||||
|
||||
private CityPicker(){}
|
||||
|
||||
public static CityPicker getInstance(){
|
||||
if (mInstance == null){
|
||||
synchronized (CityPicker.class){
|
||||
if (mInstance == null){
|
||||
mInstance = new CityPicker();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
private FragmentManager mFragmentManager;
|
||||
private Fragment mTargetFragment;
|
||||
|
||||
private boolean enableAnim;
|
||||
private int mAnimStyle;
|
||||
private LocatedCity mLocation;
|
||||
private List<HotCity> mHotCities;
|
||||
private OnPickListener mOnPickListener;
|
||||
|
||||
public CityPicker setFragmentManager(FragmentManager fm) {
|
||||
this.mFragmentManager = fm;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CityPicker setTargetFragment(Fragment targetFragment) {
|
||||
this.mTargetFragment = targetFragment;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置动画效果
|
||||
* @param animStyle
|
||||
* @return
|
||||
*/
|
||||
public CityPicker setAnimationStyle(@StyleRes int animStyle) {
|
||||
this.mAnimStyle = animStyle;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* 设置当前已经定位的城市
|
||||
* @param location
|
||||
* @return
|
||||
*/
|
||||
public CityPicker setLocatedCity(LocatedCity location) {
|
||||
this.mLocation = location;
|
||||
if (null != mCityPickerFragment) {
|
||||
if (null != location) {
|
||||
locateComplete(location, LocateState.SUCCESS);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CityPicker setHotCities(List<HotCity> data){
|
||||
this.mHotCities = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用动画效果,默认为false
|
||||
* @param enable
|
||||
* @return
|
||||
*/
|
||||
public CityPicker enableAnimation(boolean enable){
|
||||
this.enableAnim = enable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选择结果的监听器
|
||||
* @param listener
|
||||
* @return
|
||||
*/
|
||||
public CityPicker setOnPickListener(OnPickListener listener){
|
||||
this.mOnPickListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void show(){
|
||||
if (mFragmentManager == null){
|
||||
throw new UnsupportedOperationException("CityPicker:method setFragmentManager() must be called.");
|
||||
}
|
||||
FragmentTransaction ft = mFragmentManager.beginTransaction();
|
||||
final Fragment prev = mFragmentManager.findFragmentByTag(TAG);
|
||||
if (prev != null){
|
||||
ft.remove(prev).commit();
|
||||
ft = mFragmentManager.beginTransaction();
|
||||
}
|
||||
ft.addToBackStack(null);
|
||||
mCityPickerFragment = CityPickerDialogFragment.newInstance(enableAnim);
|
||||
mCityPickerFragment.setLocatedCity(mLocation);
|
||||
mCityPickerFragment.setHotCities(mHotCities);
|
||||
mCityPickerFragment.setAnimationStyle(mAnimStyle);
|
||||
mCityPickerFragment.setOnPickListener(mOnPickListener);
|
||||
if (mTargetFragment != null){
|
||||
mCityPickerFragment.setTargetFragment(mTargetFragment, 0);
|
||||
}
|
||||
mCityPickerFragment.show(ft, TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 定位完成
|
||||
* @param location
|
||||
* @param state
|
||||
*/
|
||||
public void locateComplete(LocatedCity location, @LocateState.State int state){
|
||||
CityPickerDialogFragment fragment = (CityPickerDialogFragment) mFragmentManager.findFragmentByTag(TAG);
|
||||
if (fragment != null){
|
||||
fragment.locationChanged(location, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
package com.fengliyan.uikit.picker.citypicker;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.appcompat.app.AppCompatDialogFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.picker.citypicker.adapter.CityListAdapter;
|
||||
import com.fengliyan.uikit.picker.citypicker.adapter.InnerListener;
|
||||
import com.fengliyan.uikit.picker.citypicker.adapter.OnPickListener;
|
||||
import com.fengliyan.uikit.picker.citypicker.adapter.decoration.DividerItemDecoration;
|
||||
import com.fengliyan.uikit.picker.citypicker.adapter.decoration.SectionItemDecoration;
|
||||
import com.fengliyan.uikit.picker.citypicker.db.DBManager;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.City;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.HotCity;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.LocateState;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.LocatedCity;
|
||||
import com.fengliyan.uikit.picker.citypicker.view.SideIndexBar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: Bro0cL
|
||||
* @Date: 2018/2/6 20:50
|
||||
*/
|
||||
public class CityPickerDialogFragment extends AppCompatDialogFragment implements TextWatcher,
|
||||
View.OnClickListener, SideIndexBar.OnIndexTouchedChangedListener, InnerListener {
|
||||
private View mContentView;
|
||||
private RecyclerView mRecyclerView;
|
||||
private View mEmptyView;
|
||||
private TextView mOverlayTextView;
|
||||
private SideIndexBar mIndexBar;
|
||||
private EditText mSearchBox;
|
||||
private TextView mCancelBtn;
|
||||
private ImageView mClearAllBtn;
|
||||
|
||||
private LinearLayoutManager mLayoutManager;
|
||||
private CityListAdapter mAdapter;
|
||||
private List<City> mAllCities;
|
||||
private List<HotCity> mHotCities;
|
||||
private List<City> mResults;
|
||||
|
||||
private DBManager dbManager;
|
||||
|
||||
private boolean enableAnim = false;
|
||||
private int mAnimStyle = R.style.DefaultCityPickerAnimation;
|
||||
private LocatedCity mLocatedCity;
|
||||
private int locateState;
|
||||
private OnPickListener mOnPickListener;
|
||||
|
||||
/**
|
||||
* 获取实例
|
||||
* @param enable 是否启用动画效果
|
||||
* @return
|
||||
*/
|
||||
public static CityPickerDialogFragment newInstance(boolean enable){
|
||||
final CityPickerDialogFragment fragment = new CityPickerDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putBoolean("cp_enable_anim", enable);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setStyle(STYLE_NO_TITLE, R.style.CityPickerStyle);
|
||||
|
||||
Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
enableAnim = args.getBoolean("cp_enable_anim");
|
||||
}
|
||||
|
||||
initHotCities();
|
||||
initLocatedCity();
|
||||
|
||||
dbManager = new DBManager(getContext());
|
||||
mAllCities = dbManager.getAllCities();
|
||||
mAllCities.add(0, mLocatedCity);
|
||||
//mAllCities.add(1, new HotCity("热门城市", "未知", "0"));
|
||||
mResults = mAllCities;
|
||||
}
|
||||
|
||||
private void initLocatedCity() {
|
||||
if (mLocatedCity == null){
|
||||
mLocatedCity = new LocatedCity(getString(R.string.cp_locating), "未知", "0");
|
||||
locateState = LocateState.FAILURE;
|
||||
}else{
|
||||
if (!TextUtils.isEmpty(mLocatedCity.getName())) {
|
||||
locateState = LocateState.SUCCESS;
|
||||
}else {
|
||||
mLocatedCity = new LocatedCity(getString(R.string.cp_locating), "未知", "0");
|
||||
locateState = LocateState.FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initHotCities() {
|
||||
if (mHotCities == null || mHotCities.isEmpty()) {
|
||||
mHotCities = new ArrayList<>();
|
||||
mHotCities.add(new HotCity("北京", "北京", "101010100"));
|
||||
mHotCities.add(new HotCity("上海", "上海", "101020100"));
|
||||
mHotCities.add(new HotCity("广州", "广东", "101280101"));
|
||||
mHotCities.add(new HotCity("深圳", "广东", "101280601"));
|
||||
mHotCities.add(new HotCity("天津", "天津", "101030100"));
|
||||
mHotCities.add(new HotCity("杭州", "浙江", "101210101"));
|
||||
mHotCities.add(new HotCity("南京", "江苏", "101190101"));
|
||||
mHotCities.add(new HotCity("成都", "四川", "101270101"));
|
||||
mHotCities.add(new HotCity("武汉", "湖北", "101200101"));
|
||||
}
|
||||
}
|
||||
|
||||
public void setLocatedCity(LocatedCity location){
|
||||
mLocatedCity = location;
|
||||
}
|
||||
|
||||
public void setHotCities(List<HotCity> data){
|
||||
if (data != null && !data.isEmpty()){
|
||||
this.mHotCities = data;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ResourceType")
|
||||
public void setAnimationStyle(@StyleRes int style){
|
||||
this.mAnimStyle = style <= 0 ? R.style.DefaultCityPickerAnimation : style;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
mContentView = inflater.inflate(R.layout.cp_dialog_city_picker, container, false);
|
||||
|
||||
mRecyclerView = mContentView.findViewById(R.id.cp_city_recyclerview);
|
||||
mLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
|
||||
mRecyclerView.setLayoutManager(mLayoutManager);
|
||||
mRecyclerView.setHasFixedSize(true);
|
||||
mRecyclerView.addItemDecoration(new SectionItemDecoration(getActivity(), mAllCities), 0);
|
||||
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity()), 1);
|
||||
mAdapter = new CityListAdapter(getActivity(), mAllCities, mHotCities, locateState);
|
||||
mAdapter.setInnerListener(this);
|
||||
mAdapter.setLayoutManager(mLayoutManager);
|
||||
mRecyclerView.setAdapter(mAdapter);
|
||||
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
|
||||
//确保定位城市能正常刷新
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE){
|
||||
mAdapter.refreshLocationItem();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mEmptyView = mContentView.findViewById(R.id.cp_empty_view);
|
||||
mOverlayTextView = mContentView.findViewById(R.id.cp_overlay);
|
||||
|
||||
mIndexBar = mContentView.findViewById(R.id.cp_side_index_bar);
|
||||
mIndexBar.setOverlayTextView(mOverlayTextView)
|
||||
.setOnIndexChangedListener(this);
|
||||
|
||||
mSearchBox = mContentView.findViewById(R.id.cp_search_box);
|
||||
mSearchBox.addTextChangedListener(this);
|
||||
|
||||
mCancelBtn = mContentView.findViewById(R.id.cp_cancel);
|
||||
mClearAllBtn = mContentView.findViewById(R.id.cp_clear_all);
|
||||
mCancelBtn.setOnClickListener(this);
|
||||
mClearAllBtn.setOnClickListener(this);
|
||||
|
||||
return mContentView;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||
Window window = dialog.getWindow();
|
||||
if(window != null) {
|
||||
window.getDecorView().setPadding(0, 0, 0, 0);
|
||||
window.setBackgroundDrawableResource(android.R.color.transparent);
|
||||
window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
|
||||
if (enableAnim) {
|
||||
window.setWindowAnimations(mAnimStyle);
|
||||
}
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/** 搜索框监听 */
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
String keyword = s.toString();
|
||||
if (TextUtils.isEmpty(keyword)){
|
||||
mClearAllBtn.setVisibility(View.GONE);
|
||||
mEmptyView.setVisibility(View.GONE);
|
||||
mResults = mAllCities;
|
||||
((SectionItemDecoration)(mRecyclerView.getItemDecorationAt(0))).setData(mResults);
|
||||
mAdapter.updateData(mResults);
|
||||
}else {
|
||||
mClearAllBtn.setVisibility(View.VISIBLE);
|
||||
//开始数据库查找
|
||||
mResults = dbManager.searchCity(keyword);
|
||||
((SectionItemDecoration)(mRecyclerView.getItemDecorationAt(0))).setData(mResults);
|
||||
if (mResults == null || mResults.isEmpty()){
|
||||
mEmptyView.setVisibility(View.VISIBLE);
|
||||
}else {
|
||||
mEmptyView.setVisibility(View.GONE);
|
||||
mAdapter.updateData(mResults);
|
||||
}
|
||||
}
|
||||
mRecyclerView.scrollToPosition(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
if (id == R.id.cp_cancel) {
|
||||
dismiss(-1, null);
|
||||
}else if(id == R.id.cp_clear_all){
|
||||
mSearchBox.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIndexChanged(String index, int position) {
|
||||
//滚动RecyclerView到索引位置
|
||||
mAdapter.scrollToSection(index);
|
||||
}
|
||||
|
||||
public void locationChanged(LocatedCity location, int state){
|
||||
mAdapter.updateLocateState(location, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss(int position, City data) {
|
||||
dismiss();
|
||||
if (mOnPickListener != null){
|
||||
mOnPickListener.onPick(position, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void locate(){
|
||||
if (mOnPickListener != null){
|
||||
mOnPickListener.onLocate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnPickListener(OnPickListener listener){
|
||||
this.mOnPickListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.picker.citypicker.adapter.decoration.GridItemDecoration;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.City;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.HotCity;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.LocateState;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.LocatedCity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: Bro0cL
|
||||
* @Date: 2018/2/5 12:06
|
||||
*/
|
||||
public class CityListAdapter extends RecyclerView.Adapter<CityListAdapter.BaseViewHolder> {
|
||||
private static final int VIEW_TYPE_CURRENT = 10;
|
||||
private static final int VIEW_TYPE_HOT = 11;
|
||||
|
||||
private Context mContext;
|
||||
private List<City> mData;
|
||||
private List<HotCity> mHotData;
|
||||
private int locateState;
|
||||
private InnerListener mInnerListener;
|
||||
private LinearLayoutManager mLayoutManager;
|
||||
private boolean stateChanged;
|
||||
|
||||
public CityListAdapter(Context context, List<City> data, List<HotCity> hotData, int state) {
|
||||
this.mData = data;
|
||||
this.mContext = context;
|
||||
this.mHotData = hotData;
|
||||
this.locateState = state;
|
||||
}
|
||||
|
||||
public void setLayoutManager(LinearLayoutManager manager){
|
||||
this.mLayoutManager = manager;
|
||||
}
|
||||
|
||||
public void updateData(List<City> data){
|
||||
this.mData = data;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void updateLocateState(LocatedCity location, int state){
|
||||
mData.remove(0);
|
||||
mData.add(0, location);
|
||||
stateChanged = !(locateState == state);
|
||||
locateState = state;
|
||||
refreshLocationItem();
|
||||
}
|
||||
|
||||
public void refreshLocationItem(){
|
||||
//如果定位城市的item可见则进行刷新
|
||||
if (stateChanged && mLayoutManager.findFirstVisibleItemPosition() == 0) {
|
||||
stateChanged = false;
|
||||
notifyItemChanged(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动RecyclerView到索引位置
|
||||
* @param index
|
||||
*/
|
||||
public void scrollToSection(String index){
|
||||
if (mData == null || mData.isEmpty()) return;
|
||||
if (TextUtils.isEmpty(index)) return;
|
||||
int size = mData.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (TextUtils.equals(index.substring(0, 1), mData.get(i).getSection().substring(0, 1))){
|
||||
if (mLayoutManager != null){
|
||||
mLayoutManager.scrollToPositionWithOffset(i, 0);
|
||||
if (TextUtils.equals(index.substring(0, 1), "定")) {
|
||||
//防止滚动时进行刷新
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (stateChanged) notifyItemChanged(0);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view;
|
||||
switch (viewType){
|
||||
case VIEW_TYPE_CURRENT:
|
||||
view = LayoutInflater.from(mContext).inflate(R.layout.cp_list_item_location_layout, parent, false);
|
||||
return new LocationViewHolder(view);
|
||||
/*case VIEW_TYPE_HOT:
|
||||
view = LayoutInflater.from(mContext).inflate(R.layout.cp_list_item_hot_layout, parent, false);
|
||||
return new HotViewHolder(view);*/
|
||||
default:
|
||||
view = LayoutInflater.from(mContext).inflate(R.layout.cp_list_item_default_layout, parent, false);
|
||||
return new DefaultViewHolder(view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(BaseViewHolder holder, int position) {
|
||||
if (holder == null) return;
|
||||
if (holder instanceof DefaultViewHolder){
|
||||
final int pos = holder.getAdapterPosition();
|
||||
final City data = mData.get(pos);
|
||||
if (data == null) return;
|
||||
((DefaultViewHolder)holder).name.setText(data.getName());
|
||||
((DefaultViewHolder) holder).name.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mInnerListener != null){
|
||||
mInnerListener.dismiss(pos, data);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
//定位城市
|
||||
if (holder instanceof LocationViewHolder){
|
||||
final int pos = holder.getAdapterPosition();
|
||||
final City data = mData.get(pos);
|
||||
if (data == null) return;
|
||||
//设置宽高
|
||||
DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
|
||||
int screenWidth = dm.widthPixels;
|
||||
TypedValue typedValue = new TypedValue();
|
||||
mContext.getTheme().resolveAttribute(R.attr.cpGridItemSpace, typedValue, true);
|
||||
int space = mContext.getResources().getDimensionPixelSize(R.dimen.cp_grid_item_space);
|
||||
int padding = mContext.getResources().getDimensionPixelSize(R.dimen.cp_default_padding);
|
||||
int indexBarWidth = mContext.getResources().getDimensionPixelSize(R.dimen.cp_index_bar_width);
|
||||
int itemWidth = (screenWidth - padding - space * (GridListAdapter.SPAN_COUNT - 1) - indexBarWidth) / GridListAdapter.SPAN_COUNT;
|
||||
ViewGroup.LayoutParams lp = ((LocationViewHolder) holder).container.getLayoutParams();
|
||||
lp.width = itemWidth;
|
||||
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
((LocationViewHolder) holder).container.setLayoutParams(lp);
|
||||
|
||||
switch (locateState){
|
||||
case LocateState.LOCATING:
|
||||
((LocationViewHolder) holder).current.setText(R.string.cp_locating);
|
||||
break;
|
||||
case LocateState.SUCCESS:
|
||||
((LocationViewHolder) holder).current.setText(data.getName());
|
||||
break;
|
||||
case LocateState.FAILURE:
|
||||
((LocationViewHolder) holder).current.setText(R.string.cp_locate_failed);
|
||||
break;
|
||||
}
|
||||
((LocationViewHolder) holder).container.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (locateState == LocateState.SUCCESS) {
|
||||
if (mInnerListener != null) {
|
||||
mInnerListener.dismiss(pos, data);
|
||||
}
|
||||
} else if (locateState == LocateState.FAILURE){
|
||||
// locateState = LocateState.LOCATING;
|
||||
// notifyItemChanged(0);
|
||||
if (mInnerListener != null){
|
||||
mInnerListener.locate();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
//热门城市
|
||||
/*if (holder instanceof HotViewHolder){
|
||||
final int pos = holder.getAdapterPosition();
|
||||
final City data = mData.get(pos);
|
||||
if (data == null) return;
|
||||
GridListAdapter mAdapter = new GridListAdapter(mContext, mHotData);
|
||||
mAdapter.setInnerListener(mInnerListener);
|
||||
((HotViewHolder) holder).mRecyclerView.setAdapter(mAdapter);
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mData == null ? 0 : mData.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (position == 0 && TextUtils.equals("定", mData.get(position).getSection().substring(0, 1)))
|
||||
return VIEW_TYPE_CURRENT;
|
||||
/*if (position == 1 && TextUtils.equals("热", mData.get(position).getSection().substring(0, 1)))
|
||||
return VIEW_TYPE_HOT;*/
|
||||
return super.getItemViewType(position);
|
||||
}
|
||||
|
||||
public void setInnerListener(InnerListener listener){
|
||||
this.mInnerListener = listener;
|
||||
}
|
||||
|
||||
static class BaseViewHolder extends RecyclerView.ViewHolder{
|
||||
BaseViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DefaultViewHolder extends BaseViewHolder{
|
||||
TextView name;
|
||||
|
||||
DefaultViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
name = itemView.findViewById(R.id.cp_list_item_name);
|
||||
}
|
||||
}
|
||||
|
||||
public static class HotViewHolder extends BaseViewHolder {
|
||||
RecyclerView mRecyclerView;
|
||||
|
||||
HotViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mRecyclerView = itemView.findViewById(R.id.cp_hot_list);
|
||||
mRecyclerView.setHasFixedSize(true);
|
||||
mRecyclerView.setLayoutManager(new GridLayoutManager(itemView.getContext(),
|
||||
GridListAdapter.SPAN_COUNT, LinearLayoutManager.VERTICAL, false));
|
||||
int space = itemView.getContext().getResources().getDimensionPixelSize(R.dimen.cp_grid_item_space);
|
||||
mRecyclerView.addItemDecoration(new GridItemDecoration(GridListAdapter.SPAN_COUNT,
|
||||
space));
|
||||
}
|
||||
}
|
||||
|
||||
public static class LocationViewHolder extends BaseViewHolder {
|
||||
FrameLayout container;
|
||||
TextView current;
|
||||
|
||||
LocationViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
container = itemView.findViewById(R.id.cp_list_item_location_layout);
|
||||
current = itemView.findViewById(R.id.cp_list_item_location);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.City;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.HotCity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: Bro0cL
|
||||
* @Date: 2018/2/8 21:22
|
||||
*/
|
||||
public class GridListAdapter extends RecyclerView.Adapter<GridListAdapter.GridViewHolder>{
|
||||
public static final int SPAN_COUNT = 3;
|
||||
|
||||
private Context mContext;
|
||||
private List<HotCity> mData;
|
||||
private InnerListener mInnerListener;
|
||||
|
||||
public GridListAdapter(Context context, List<HotCity> data) {
|
||||
this.mContext = context;
|
||||
this.mData = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GridViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(mContext).inflate(R.layout.cp_grid_item_layout, parent, false);
|
||||
return new GridViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(GridViewHolder holder, int position) {
|
||||
final int pos = holder.getAdapterPosition();
|
||||
final City data = mData.get(pos);
|
||||
if (data == null) return;
|
||||
//设置item宽高
|
||||
DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
|
||||
int screenWidth = dm.widthPixels;
|
||||
TypedValue typedValue = new TypedValue();
|
||||
mContext.getTheme().resolveAttribute(R.attr.cpGridItemSpace, typedValue, true);
|
||||
int space = mContext.getResources().getDimensionPixelSize(typedValue.resourceId);
|
||||
int padding = mContext.getResources().getDimensionPixelSize(R.dimen.cp_default_padding);
|
||||
int indexBarWidth = mContext.getResources().getDimensionPixelSize(R.dimen.cp_index_bar_width);
|
||||
int itemWidth = (screenWidth - padding - space * (SPAN_COUNT - 1) - indexBarWidth) / SPAN_COUNT;
|
||||
ViewGroup.LayoutParams lp = holder.container.getLayoutParams();
|
||||
lp.width = itemWidth;
|
||||
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
holder.container.setLayoutParams(lp);
|
||||
|
||||
holder.name.setText(data.getName());
|
||||
holder.container.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mInnerListener != null){
|
||||
mInnerListener.dismiss(pos, data);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mData == null ? 0 : mData.size();
|
||||
}
|
||||
|
||||
public static class GridViewHolder extends RecyclerView.ViewHolder{
|
||||
FrameLayout container;
|
||||
TextView name;
|
||||
|
||||
public GridViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
container = itemView.findViewById(R.id.cp_grid_item_layout);
|
||||
name = itemView.findViewById(R.id.cp_gird_item_name);
|
||||
}
|
||||
}
|
||||
|
||||
public void setInnerListener(InnerListener listener){
|
||||
this.mInnerListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.adapter;
|
||||
|
||||
import com.fengliyan.uikit.picker.citypicker.model.City;
|
||||
|
||||
public interface InnerListener {
|
||||
void dismiss(int position, City data);
|
||||
void locate();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.adapter;
|
||||
|
||||
|
||||
import com.fengliyan.uikit.picker.citypicker.model.City;
|
||||
|
||||
public interface OnPickListener {
|
||||
void onPick(int position, City data);
|
||||
void onLocate();
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.adapter.decoration;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
private float dividerHeight;
|
||||
private Paint mPaint;
|
||||
|
||||
public DividerItemDecoration(Context context) {
|
||||
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
TypedValue typedValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(R.attr.cpSectionBackground, typedValue, true);
|
||||
mPaint.setColor(context.getResources().getColor(typedValue.resourceId));
|
||||
dividerHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.5f, context.getResources().getDisplayMetrics());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
|
||||
super.getItemOffsets(outRect, view, parent, state);
|
||||
outRect.bottom = (int) dividerHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
int childCount = parent.getChildCount();
|
||||
int left = parent.getPaddingLeft();
|
||||
int right = parent.getWidth() - parent.getPaddingRight();
|
||||
|
||||
for (int i = 0; i < childCount - 1; i++) {
|
||||
View view = parent.getChildAt(i);
|
||||
float top = view.getBottom();
|
||||
float bottom = view.getBottom() + dividerHeight;
|
||||
c.drawRect(left, top, right, bottom, mPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.adapter.decoration;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class GridItemDecoration extends RecyclerView.ItemDecoration{
|
||||
private int mSpanCount;
|
||||
private int mSpace;
|
||||
|
||||
public GridItemDecoration(int spanCount, int space) {
|
||||
this.mSpanCount = spanCount;
|
||||
this.mSpace = space;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
|
||||
int position = parent.getChildAdapterPosition(view);
|
||||
int column = position % mSpanCount;
|
||||
|
||||
outRect.left = column * mSpace / mSpanCount;
|
||||
outRect.right = mSpace - (column + 1) * mSpace / mSpanCount;
|
||||
if (position >= mSpanCount) {
|
||||
outRect.top = mSpace;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.adapter.decoration;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.text.TextPaint;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
import com.fengliyan.uikit.picker.citypicker.model.City;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SectionItemDecoration extends RecyclerView.ItemDecoration {
|
||||
private List<City> mData;
|
||||
private Paint mBgPaint;
|
||||
private TextPaint mTextPaint;
|
||||
private Rect mBounds;
|
||||
|
||||
private int mSectionHeight;
|
||||
private int mBgColor;
|
||||
private int mTextColor;
|
||||
private int mTextSize;
|
||||
|
||||
public SectionItemDecoration(Context context, List<City> data) {
|
||||
this.mData = data;
|
||||
TypedValue typedValue = new TypedValue();
|
||||
|
||||
context.getTheme().resolveAttribute(R.attr.cpSectionBackground, typedValue, true);
|
||||
mBgColor = context.getResources().getColor(typedValue.resourceId);
|
||||
|
||||
context.getTheme().resolveAttribute(R.attr.cpSectionHeight, typedValue, true);
|
||||
mSectionHeight = context.getResources().getDimensionPixelSize(typedValue.resourceId);
|
||||
|
||||
context.getTheme().resolveAttribute(R.attr.cpSectionTextSize, typedValue, true);
|
||||
mTextSize = context.getResources().getDimensionPixelSize(typedValue.resourceId);
|
||||
|
||||
context.getTheme().resolveAttribute(R.attr.cpSectionTextColor, typedValue, true);
|
||||
mTextColor = context.getResources().getColor(typedValue.resourceId);
|
||||
|
||||
mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mBgPaint.setColor(mBgColor);
|
||||
|
||||
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||
mTextPaint.setTextSize(mTextSize);
|
||||
mTextPaint.setColor(mTextColor);
|
||||
|
||||
mBounds = new Rect();
|
||||
}
|
||||
|
||||
public void setData(List<City> data) {
|
||||
this.mData = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
super.onDraw(c, parent, state);
|
||||
final int left = parent.getPaddingLeft();
|
||||
final int right = parent.getWidth() - parent.getPaddingRight();
|
||||
final int childCount = parent.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = parent.getChildAt(i);
|
||||
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
|
||||
.getLayoutParams();
|
||||
int position = params.getViewLayoutPosition();
|
||||
if (mData != null && !mData.isEmpty() && position <= mData.size() - 1 && position > -1) {
|
||||
if (position == 0) {
|
||||
drawSection(c, left, right, child, params, position);
|
||||
} else {
|
||||
if (null != mData.get(position).getSection()
|
||||
&& !mData.get(position).getSection().equals(mData.get(position - 1).getSection())) {
|
||||
drawSection(c, left, right, child, params, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawSection(Canvas c, int left, int right, View child,
|
||||
RecyclerView.LayoutParams params, int position) {
|
||||
c.drawRect(left,
|
||||
child.getTop() - params.topMargin - mSectionHeight,
|
||||
right,
|
||||
child.getTop() - params.topMargin, mBgPaint);
|
||||
mTextPaint.getTextBounds(mData.get(position).getSection(),
|
||||
0,
|
||||
mData.get(position).getSection().length(),
|
||||
mBounds);
|
||||
c.drawText(mData.get(position).getSection(),
|
||||
child.getPaddingLeft(),
|
||||
child.getTop() - params.topMargin - (mSectionHeight / 2 - mBounds.height() / 2),
|
||||
mTextPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
int pos = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();
|
||||
if (pos < 0) return;
|
||||
if (mData == null || mData.isEmpty()) return;
|
||||
String section = mData.get(pos).getSection();
|
||||
View child = parent.findViewHolderForLayoutPosition(pos).itemView;
|
||||
|
||||
boolean flag = false;
|
||||
if ((pos + 1) < mData.size()) {
|
||||
if (null != section && !section.equals(mData.get(pos + 1).getSection())) {
|
||||
if (child.getHeight() + child.getTop() < mSectionHeight) {
|
||||
c.save();
|
||||
flag = true;
|
||||
c.translate(0, child.getHeight() + child.getTop() - mSectionHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
c.drawRect(parent.getPaddingLeft(),
|
||||
parent.getPaddingTop(),
|
||||
parent.getRight() - parent.getPaddingRight(),
|
||||
parent.getPaddingTop() + mSectionHeight, mBgPaint);
|
||||
mTextPaint.getTextBounds(section, 0, section.length(), mBounds);
|
||||
c.drawText(section,
|
||||
child.getPaddingLeft(),
|
||||
parent.getPaddingTop() + mSectionHeight - (mSectionHeight / 2 - mBounds.height() / 2),
|
||||
mTextPaint);
|
||||
if (flag)
|
||||
c.restore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
|
||||
super.getItemOffsets(outRect, view, parent, state);
|
||||
int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
|
||||
if (mData != null && !mData.isEmpty() && position <= mData.size() - 1 && position > -1) {
|
||||
if (position == 0) {
|
||||
outRect.set(0, mSectionHeight, 0, 0);
|
||||
} else {
|
||||
if (null != mData.get(position).getSection()
|
||||
&& !mData.get(position).getSection().equals(mData.get(position - 1).getSection())) {
|
||||
outRect.set(0, mSectionHeight, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.db;
|
||||
|
||||
public class DBConfig {
|
||||
public static final String DB_NAME_V1 = "china_cities_v2.db";
|
||||
public static final String DB_NAME_V2 = "china_cities_v3.db";
|
||||
public static final String LATEST_DB_NAME = DB_NAME_V2;
|
||||
|
||||
public static final String TABLE_NAME = "cities";
|
||||
|
||||
public static final String COLUMN_C_NAME = "c_name";
|
||||
public static final String COLUMN_C_PROVINCE = "c_province";
|
||||
public static final String COLUMN_C_PINYIN = "c_pinyin";
|
||||
public static final String COLUMN_C_CODE = "c_code";
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.db;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.fengliyan.uikit.picker.citypicker.model.City;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static com.fengliyan.uikit.picker.citypicker.db.DBConfig.COLUMN_C_CODE;
|
||||
import static com.fengliyan.uikit.picker.citypicker.db.DBConfig.COLUMN_C_NAME;
|
||||
import static com.fengliyan.uikit.picker.citypicker.db.DBConfig.COLUMN_C_PINYIN;
|
||||
import static com.fengliyan.uikit.picker.citypicker.db.DBConfig.COLUMN_C_PROVINCE;
|
||||
import static com.fengliyan.uikit.picker.citypicker.db.DBConfig.DB_NAME_V1;
|
||||
import static com.fengliyan.uikit.picker.citypicker.db.DBConfig.LATEST_DB_NAME;
|
||||
import static com.fengliyan.uikit.picker.citypicker.db.DBConfig.TABLE_NAME;
|
||||
|
||||
/**
|
||||
* Author Bro0cL on 2016/1/26.
|
||||
*/
|
||||
public class DBManager {
|
||||
private static final int BUFFER_SIZE = 1024;
|
||||
|
||||
private String DB_PATH;
|
||||
private Context mContext;
|
||||
|
||||
public DBManager(Context context) {
|
||||
this.mContext = context;
|
||||
DB_PATH = File.separator + "data"
|
||||
+ Environment.getDataDirectory().getAbsolutePath() + File.separator
|
||||
+ context.getPackageName() + File.separator + "databases" + File.separator;
|
||||
copyDBFile();
|
||||
}
|
||||
|
||||
private void copyDBFile(){
|
||||
File dir = new File(DB_PATH);
|
||||
if (!dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
//如果旧版数据库存在,则删除
|
||||
File dbV1 = new File(DB_PATH + DB_NAME_V1);
|
||||
if (dbV1.exists()){
|
||||
dbV1.delete();
|
||||
}
|
||||
//创建新版本数据库
|
||||
File dbFile = new File(DB_PATH + LATEST_DB_NAME);
|
||||
if (!dbFile.exists()){
|
||||
InputStream is;
|
||||
OutputStream os;
|
||||
try {
|
||||
is = mContext.getResources().getAssets().open(LATEST_DB_NAME);
|
||||
os = new FileOutputStream(dbFile);
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int length;
|
||||
while ((length = is.read(buffer, 0, buffer.length)) > 0){
|
||||
os.write(buffer, 0, length);
|
||||
}
|
||||
os.flush();
|
||||
os.close();
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<City> getAllCities(){
|
||||
List<City> result = new ArrayList<>();
|
||||
try {
|
||||
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH + LATEST_DB_NAME, null);
|
||||
Cursor cursor = db.rawQuery("select * from " + TABLE_NAME, null);
|
||||
City city;
|
||||
while (cursor.moveToNext()){
|
||||
String name = cursor.getString(cursor.getColumnIndex(COLUMN_C_NAME));
|
||||
String province = cursor.getString(cursor.getColumnIndex(COLUMN_C_PROVINCE));
|
||||
String pinyin = cursor.getString(cursor.getColumnIndex(COLUMN_C_PINYIN));
|
||||
String code = cursor.getString(cursor.getColumnIndex(COLUMN_C_CODE));
|
||||
city = new City(name, province, pinyin, code);
|
||||
result.add(city);
|
||||
}
|
||||
cursor.close();
|
||||
db.close();
|
||||
Collections.sort(result, new CityComparator());
|
||||
|
||||
}catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<City> searchCity(final String keyword){
|
||||
List<City> result = new ArrayList<>();
|
||||
try {
|
||||
String sql = "select * from " + TABLE_NAME + " where "
|
||||
+ COLUMN_C_NAME + " like ? " + "or "
|
||||
+ COLUMN_C_PINYIN + " like ? ";
|
||||
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH + LATEST_DB_NAME, null);
|
||||
Cursor cursor = db.rawQuery(sql, new String[]{"%"+keyword+"%", keyword+"%"});
|
||||
|
||||
|
||||
while (cursor.moveToNext()){
|
||||
String name = cursor.getString(cursor.getColumnIndex(COLUMN_C_NAME));
|
||||
String province = cursor.getString(cursor.getColumnIndex(COLUMN_C_PROVINCE));
|
||||
String pinyin = cursor.getString(cursor.getColumnIndex(COLUMN_C_PINYIN));
|
||||
String code = cursor.getString(cursor.getColumnIndex(COLUMN_C_CODE));
|
||||
City city = new City(name, province, pinyin, code);
|
||||
result.add(city);
|
||||
}
|
||||
cursor.close();
|
||||
db.close();
|
||||
CityComparator comparator = new CityComparator();
|
||||
Collections.sort(result, comparator);
|
||||
}catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* sort by a-z
|
||||
*/
|
||||
private class CityComparator implements Comparator<City>{
|
||||
@Override
|
||||
public int compare(City lhs, City rhs) {
|
||||
String a = lhs.getPinyin().substring(0, 1);
|
||||
String b = rhs.getPinyin().substring(0, 1);
|
||||
return a.compareTo(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.model;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* author Bro0cL on 2016/1/26.
|
||||
*/
|
||||
public class City {
|
||||
private String name;
|
||||
private String province;
|
||||
private String pinyin;
|
||||
private String code;
|
||||
|
||||
public City(String name, String province, String pinyin, String code) {
|
||||
this.name = name;
|
||||
this.province = province;
|
||||
this.pinyin = pinyin;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取悬浮栏文本,(#、定位、热门 需要特殊处理)
|
||||
* @return
|
||||
*/
|
||||
public String getSection(){
|
||||
if (TextUtils.isEmpty(pinyin)) {
|
||||
return "#";
|
||||
} else {
|
||||
String c = pinyin.substring(0, 1);
|
||||
Pattern p = Pattern.compile("[a-zA-Z]");
|
||||
Matcher m = p.matcher(c);
|
||||
if (m.matches()) {
|
||||
return c.toUpperCase();
|
||||
}
|
||||
//在添加定位和热门数据时设置的section就是‘定’、’热‘开头
|
||||
else if (TextUtils.equals(c, "定") || TextUtils.equals(c, "热"))
|
||||
return pinyin;
|
||||
else
|
||||
return "#";
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPinyin() {
|
||||
return pinyin;
|
||||
}
|
||||
|
||||
public void setPinyin(String pinyin) {
|
||||
this.pinyin = pinyin;
|
||||
}
|
||||
|
||||
public String getProvince() {
|
||||
return province;
|
||||
}
|
||||
|
||||
public void setProvince(String province) {
|
||||
this.province = province;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.model;
|
||||
|
||||
public class HotCity extends City {
|
||||
|
||||
public HotCity(String name, String province, String code) {
|
||||
super(name, province, "热门城市", code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.model;
|
||||
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
public class LocateState {
|
||||
public static final int LOCATING = 123;
|
||||
public static final int SUCCESS = 132;
|
||||
public static final int FAILURE = 321;
|
||||
|
||||
@IntDef({SUCCESS, FAILURE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface State{}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.model;
|
||||
|
||||
public class LocatedCity extends City {
|
||||
|
||||
public LocatedCity(String name, String province, String code) {
|
||||
super(name, province, "定位城市", code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.fengliyan.uikit.picker.citypicker.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author: Bro0cL
|
||||
* @Date: 2018/2/8 10:56
|
||||
*/
|
||||
public class SideIndexBar extends View{
|
||||
private static final String[] DEFAULT_INDEX_ITEMS = {"定位", /*"热门",*/ "A", "B", "C", "D", "E", "F", "G", "H",
|
||||
"I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "#"};
|
||||
|
||||
private List<String> mIndexItems;
|
||||
private float mItemHeight; //每个index的高度
|
||||
private int mTextSize; //sp
|
||||
private int mTextColor;
|
||||
private int mTextTouchedColor;
|
||||
private int mCurrentIndex = -1;
|
||||
|
||||
private Paint mPaint;
|
||||
private Paint mTouchedPaint;
|
||||
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
private float mTopMargin; //居中绘制,文字绘制起点和控件顶部的间隔
|
||||
|
||||
private TextView mOverlayTextView;
|
||||
private OnIndexTouchedChangedListener mOnIndexChangedListener;
|
||||
|
||||
public SideIndexBar(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public SideIndexBar(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public SideIndexBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
mIndexItems = new ArrayList<>();
|
||||
mIndexItems.addAll(Arrays.asList(DEFAULT_INDEX_ITEMS));
|
||||
|
||||
TypedValue typedValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(R.attr.cpIndexBarTextSize, typedValue, true);
|
||||
mTextSize = context.getResources().getDimensionPixelSize(typedValue.resourceId);
|
||||
|
||||
context.getTheme().resolveAttribute(R.attr.cpIndexBarNormalTextColor, typedValue, true);
|
||||
mTextColor = context.getResources().getColor(typedValue.resourceId);
|
||||
|
||||
context.getTheme().resolveAttribute(R.attr.cpIndexBarSelectedTextColor, typedValue, true);
|
||||
mTextTouchedColor = context.getResources().getColor(typedValue.resourceId);
|
||||
|
||||
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaint.setTextSize(mTextSize);
|
||||
mPaint.setColor(mTextColor);
|
||||
|
||||
mTouchedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mTouchedPaint.setTextSize(mTextSize);
|
||||
mTouchedPaint.setColor(mTextTouchedColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
String index;
|
||||
for (int i = 0; i < mIndexItems.size(); i++) {
|
||||
index = mIndexItems.get(i);
|
||||
Paint.FontMetrics fm = mPaint.getFontMetrics();
|
||||
canvas.drawText(index,
|
||||
(mWidth - mPaint.measureText(index)) / 2,
|
||||
mItemHeight / 2 + (fm.bottom-fm.top) / 2 - fm.bottom + mItemHeight * i + mTopMargin,
|
||||
i == mCurrentIndex ? mTouchedPaint : mPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
mWidth = getWidth();
|
||||
mHeight = Math.max(h, oldh);
|
||||
mItemHeight = mHeight / mIndexItems.size();
|
||||
mTopMargin = (mHeight - mItemHeight * mIndexItems.size()) / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performClick() {
|
||||
return super.performClick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
performClick();
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
float y = event.getY();
|
||||
int indexSize = mIndexItems.size();
|
||||
int touchedIndex = (int) (y / mItemHeight);
|
||||
if (touchedIndex < 0) {
|
||||
touchedIndex = 0;
|
||||
}else if (touchedIndex >= indexSize) {
|
||||
touchedIndex = indexSize - 1;
|
||||
}
|
||||
if (mOnIndexChangedListener != null && touchedIndex >= 0 && touchedIndex < indexSize){
|
||||
if (touchedIndex != mCurrentIndex) {
|
||||
mCurrentIndex = touchedIndex;
|
||||
if (mOverlayTextView != null){
|
||||
mOverlayTextView.setVisibility(VISIBLE);
|
||||
mOverlayTextView.setText(mIndexItems.get(touchedIndex));
|
||||
}
|
||||
mOnIndexChangedListener.onIndexChanged(mIndexItems.get(touchedIndex), touchedIndex);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mCurrentIndex = -1;
|
||||
if (mOverlayTextView != null){
|
||||
mOverlayTextView.setVisibility(GONE);
|
||||
}
|
||||
invalidate();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public SideIndexBar setOverlayTextView(TextView overlay){
|
||||
this.mOverlayTextView = overlay;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SideIndexBar setOnIndexChangedListener(OnIndexTouchedChangedListener listener){
|
||||
this.mOnIndexChangedListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public interface OnIndexTouchedChangedListener{
|
||||
void onIndexChanged(String index, int position);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.fengliyan.uikit.refresh;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.fengliyan.uikit.R;
|
||||
|
||||
/**
|
||||
* Created by abby on 2017/1/7.
|
||||
*/
|
||||
|
||||
public class LoadMoreListView extends ListView {
|
||||
private TextView mLoadMoreHint;
|
||||
private View mLoadMoreView;
|
||||
private View mEmptyView;
|
||||
private OnDataLoadingListener mListener;
|
||||
private boolean hasScrollView;
|
||||
|
||||
|
||||
public interface OnDataLoadingListener {
|
||||
void loading();
|
||||
}
|
||||
|
||||
public void setOnDataLoadingListener(OnDataLoadingListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public LoadMoreListView(Context context) {
|
||||
super(context);
|
||||
initView();
|
||||
}
|
||||
|
||||
public LoadMoreListView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LoadMoreListView);
|
||||
hasScrollView = a.getBoolean(R.styleable.LoadMoreListView_has_scroview, false);
|
||||
initView();
|
||||
}
|
||||
|
||||
public LoadMoreListView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LoadMoreListView);
|
||||
hasScrollView = a.getBoolean(R.styleable.LoadMoreListView_has_scroview, false);
|
||||
initView();
|
||||
}
|
||||
|
||||
public void setScollView(boolean hasScrollView) {
|
||||
this.hasScrollView = hasScrollView;
|
||||
}
|
||||
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int expandSpec = 0;
|
||||
if (hasScrollView) {
|
||||
// 设置最大高度(例如屏幕高度的 80%)
|
||||
int maxHeight = (int) (getResources().getDisplayMetrics().heightPixels * 0.3f);
|
||||
int specSize = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
if (specSize > maxHeight) {
|
||||
expandSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
|
||||
} else {
|
||||
expandSpec = MeasureSpec.makeMeasureSpec(specSize, MeasureSpec.AT_MOST);
|
||||
}
|
||||
} else {
|
||||
expandSpec = heightMeasureSpec;
|
||||
}
|
||||
|
||||
super.onMeasure(widthMeasureSpec, expandSpec);
|
||||
}
|
||||
|
||||
|
||||
public void initView() {
|
||||
mLoadMoreView = LayoutInflater.from(getContext()).inflate(R.layout.load_more_hint, null);
|
||||
mLoadMoreHint = (TextView) mLoadMoreView.findViewById(R.id.load_more_text);
|
||||
addFooterView(mLoadMoreView);
|
||||
mLoadMoreView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void onDataReady() {
|
||||
mLoadMoreView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void onDataLoading() {
|
||||
mLoadMoreView.setVisibility(View.VISIBLE);
|
||||
mLoadMoreHint.setText("正在加载更多...");
|
||||
if (null != mListener) {
|
||||
mListener.loading();
|
||||
}
|
||||
}
|
||||
|
||||
public void onDataLoading(String message) {
|
||||
mLoadMoreView.setVisibility(View.VISIBLE);
|
||||
mLoadMoreHint.setText(message);
|
||||
if (null != mListener) {
|
||||
mListener.loading();
|
||||
}
|
||||
}
|
||||
|
||||
public void onNoDataLoaded() {
|
||||
mLoadMoreView.setVisibility(View.VISIBLE);
|
||||
mLoadMoreHint.setText("我的陪伴 · 让生活更精彩");
|
||||
}
|
||||
|
||||
public void onNoData() {
|
||||
mLoadMoreView.setVisibility(View.VISIBLE);
|
||||
mLoadMoreHint.setText("暂无数据 ~ ~ ");
|
||||
}
|
||||
|
||||
public void addLoadMoreView(View v) {
|
||||
removeFooterView(mLoadMoreView);
|
||||
addFooterView(v);
|
||||
mLoadMoreView = v;
|
||||
}
|
||||
|
||||
public View getLoadMoreView() {
|
||||
return mLoadMoreView;
|
||||
}
|
||||
|
||||
public void setAdapter(ListAdapter adapter) {
|
||||
super.setAdapter(adapter);
|
||||
// mEmptyView = LayoutInflater.from(getContext()).inflate(R.layout.empty_view, null);
|
||||
//ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
//mEmptyView.setLayoutParams(params);
|
||||
//((ViewGroup)super.getParent()).addView(mEmptyView);
|
||||
//super.setEmptyView(mEmptyView);
|
||||
}
|
||||
|
||||
public void dimissEmptyView() {
|
||||
if (null != mEmptyView) {
|
||||
mEmptyView.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void colorLoadMoreViewByResId(int resId) {
|
||||
mLoadMoreView.setBackgroundResource(resId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.fengliyan.uikit.scroll;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
/**
|
||||
* Created by chenqihong on 2017/3/9.
|
||||
*/
|
||||
|
||||
public class ObservedScrollView extends ScrollView {
|
||||
private ScrollViewListener mScrollViewListener = null;
|
||||
|
||||
public interface ScrollViewListener{
|
||||
void onScrollChanged(View scrollView, int x, int y, int oldx, int oldy);
|
||||
void onBottom();
|
||||
void onTop();
|
||||
}
|
||||
|
||||
public ObservedScrollView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ObservedScrollView(Context context, AttributeSet attrs,
|
||||
int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
public ObservedScrollView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setScrollViewListener(ScrollViewListener scrollViewListener) {
|
||||
this.mScrollViewListener = scrollViewListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
|
||||
super.onScrollChanged(x, y, oldx, oldy);
|
||||
if (mScrollViewListener != null) {
|
||||
mScrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
|
||||
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
|
||||
if (scrollY == 0) {
|
||||
if(clampedY){
|
||||
if (mScrollViewListener != null) {
|
||||
mScrollViewListener.onTop();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(clampedY){
|
||||
if (mScrollViewListener != null) {
|
||||
mScrollViewListener.onBottom();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user