Repository: Hankkin/TaoSchool Branch: master Commit: d52c81de284a Files: 131 Total size: 444.7 KB Directory structure: gitextract__kczv6ja/ ├── .gitignore ├── .idea/ │ ├── compiler.xml │ ├── copyright/ │ │ └── profiles_settings.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── README.md ├── app/ │ ├── .gitignore │ ├── app.iml │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── hankkin/ │ │ └── compustrading/ │ │ └── ApplicationTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── hankkin/ │ │ │ └── compustrading/ │ │ │ ├── Application/ │ │ │ │ └── MyApplication.java │ │ │ ├── MainActivity.java │ │ │ ├── User/ │ │ │ │ └── UserBean.java │ │ │ ├── Utils/ │ │ │ │ ├── BitmapUtils.java │ │ │ │ └── HankkinUtils.java │ │ │ ├── activity/ │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── LoginActivity.java │ │ │ │ ├── MainShowActivity.java │ │ │ │ ├── NewProductActivity.java │ │ │ │ ├── PersonActivity.java │ │ │ │ ├── ProdectDetailActivity.java │ │ │ │ ├── RegisterActivity.java │ │ │ │ ├── SearchProActivity.java │ │ │ │ └── SplasActivity.java │ │ │ ├── adapter/ │ │ │ │ ├── CategoryFragmentAdapter.java │ │ │ │ ├── PersonInfoAdapter.java │ │ │ │ └── ProductAdapter.java │ │ │ ├── fragment/ │ │ │ │ └── CateDetailFragment.java │ │ │ ├── interface/ │ │ │ │ ├── FileUploadListener.java │ │ │ │ └── ScrollDirectionListener.java │ │ │ ├── model/ │ │ │ │ ├── Category.java │ │ │ │ ├── Person.java │ │ │ │ ├── PersonShow.java │ │ │ │ └── Product.java │ │ │ ├── sharepreference/ │ │ │ │ └── MySP.java │ │ │ ├── slidingmenu/ │ │ │ │ ├── CustomViewAbove.java │ │ │ │ ├── CustomViewBehind.java │ │ │ │ ├── SlidingActivityBase.java │ │ │ │ ├── SlidingActivityHelper.java │ │ │ │ ├── SlidingMenu.java │ │ │ │ └── SlidingPreferenceActivity.java │ │ │ └── view/ │ │ │ ├── CollapsingAvatarToolbar.java │ │ │ ├── PagerSlidingTabStrip.java │ │ │ ├── RippleView.java │ │ │ ├── RoundedImageView.java │ │ │ ├── floatbutton/ │ │ │ │ ├── AbsListViewScrollDetector.java │ │ │ │ ├── AddFloatingActionButton.java │ │ │ │ ├── FloatingActionButton.java │ │ │ │ ├── FloatingActionsMenu.java │ │ │ │ ├── FloatingActionsMenuHidable.java │ │ │ │ └── TouchDelegateGroup.java │ │ │ ├── ipulltozoom/ │ │ │ │ ├── IPullToZoom.java │ │ │ │ ├── PullToZoomBase.java │ │ │ │ └── PullToZoomScrollViewEx.java │ │ │ └── refreshload/ │ │ │ ├── MetaballView.java │ │ │ └── RefreshLayout.java │ │ └── res/ │ │ ├── anim/ │ │ │ ├── anim.xml │ │ │ └── list_anim.xml │ │ ├── drawable/ │ │ │ ├── bmob_update_button_cancel_bg_focused.xml │ │ │ ├── bmob_update_button_cancel_bg_normal.xml │ │ │ ├── bmob_update_button_cancel_bg_selector.xml │ │ │ ├── bmob_update_button_cancel_bg_tap.xml │ │ │ ├── bmob_update_button_check_selector.xml │ │ │ ├── bmob_update_button_close_bg_selector.xml │ │ │ ├── bmob_update_button_ok_bg_focused.xml │ │ │ ├── bmob_update_button_ok_bg_normal.xml │ │ │ ├── bmob_update_button_ok_bg_selector.xml │ │ │ ├── bmob_update_button_ok_bg_tap.xml │ │ │ ├── bmob_update_dialog_bg.xml │ │ │ ├── btn_login_background.xml │ │ │ ├── fab_label_background.xml │ │ │ ├── login_et_background.xml │ │ │ ├── normal_white_bg.xml │ │ │ ├── shadow.xml │ │ │ └── tab_background.xml │ │ ├── layout/ │ │ │ ├── activity_login.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_main_show.xml │ │ │ ├── activity_new_product.xml │ │ │ ├── activity_person.xml │ │ │ ├── activity_prodect_detail.xml │ │ │ ├── activity_product_detail.xml │ │ │ ├── activity_register.xml │ │ │ ├── activity_search_pro.xml │ │ │ ├── activity_splas.xml │ │ │ ├── bmob_update_dialog.xml │ │ │ ├── fragment_item.xml │ │ │ ├── listview_footer.xml │ │ │ ├── listview_personinfo.xml │ │ │ ├── loading.xml │ │ │ ├── lv_product_item.xml │ │ │ ├── profile_contect_view.xml │ │ │ ├── profile_head_view.xml │ │ │ ├── profile_zoom_view.xml │ │ │ ├── slidingmenumain.xml │ │ │ └── view_select_img.xml │ │ ├── menu/ │ │ │ ├── menu_login.xml │ │ │ ├── menu_main.xml │ │ │ ├── menu_new_product.xml │ │ │ ├── menu_person.xml │ │ │ ├── menu_prodect_detail.xml │ │ │ ├── menu_register.xml │ │ │ ├── menu_search.xml │ │ │ └── menu_splas.xml │ │ ├── values/ │ │ │ ├── arrays.xml │ │ │ ├── attrs.xml │ │ │ ├── bmob_common_strings.xml │ │ │ ├── bmob_update_string.xml │ │ │ ├── color.xml │ │ │ ├── dimens.xml │ │ │ ├── ids.xml │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── texsize.xml │ │ └── values-w820dp/ │ │ └── dimens.xml │ └── test/ │ └── java/ │ └── com/ │ └── hankkin/ │ └── compustrading/ │ └── ExampleUnitTest.java ├── app-release.apk ├── build.gradle ├── compaus.jks ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures ================================================ FILE: .idea/compiler.xml ================================================ ================================================ FILE: .idea/copyright/profiles_settings.xml ================================================ ================================================ FILE: .idea/encodings.xml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/modules.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: README.md ================================================ **该APP已经不再维护了,如果您需要的话 请看 https://github.com/Hankkin/Reading** *Reading是一款基于WanAndroid OpenApi开发的阅读类工具,如果你是一个热衷于Android开发者,那么这款软件能帮助你阅读精品Android文章。 同时Reading中还包含"英文单词"、"账号本子"、"天气"、"查单词"、"快递查询"等小工具。项目基于"Kotlin+MVP"架构开发,风格大概也许属于Material Desgin原质化风格,包含主题颜色切换、百变Logo、 等功能。在此感谢WanAndroid的OpenApi,以及其它开源项目的贡献。 * # TaoSchool 一款基于Material Desgin设计的APP ###look at the screenshot: 高仿微信群聊头像 高仿微信群聊头像 ###download: 网站: http://hankkin.bmob.cn PRE: http://pre.im/x9nH 360开发者平台: http://zhushou.360.cn/detail/index/soft_id/3181637?recrefer=SE_D_%E6%B7%98School 百度开发平台 http://shouji.baidu.com/soft/item?docid=8561791&from=&f=search_app_淘School%40list_1_title%401%40header_all_input ###tech: 1.Android Support Desgin CollapsingAvatarToolbar 头像随ListView滚动缩回到ActionBar特效 TextInputLayout带动画的输入框 2.ActionBarDrawerToggle、DrawerLayout、ActionBar 结合 3.RippleEffect水波纹效果 4.PagerSlidingTabStrip+viewpager实现选项卡左右滑动 5.FloatActiconButton悬浮按钮实现仿钉钉悬浮按钮 6.PullToZoomScrollView实现下拉自动放大头部View 7.materialdialog实现的对话框 8.MaterialSpinner实现的带效果的spinner 9.butterknife注解框架 CSDN博客地址:http://blog.csdn.net/lyhhj/article/details/50413625 微信:huang1019283569 ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/app.iml ================================================ ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion "25.0.2" useLibrary 'org.apache.http.legacy' defaultConfig { applicationId "com.hankkin.compustrading" minSdkVersion 14 targetSdkVersion 22 versionCode 3 versionName "1.1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:25.2.0' compile 'cn.bmob.android:bmob-sdk:3.5.5' compile 'cn.bmob.android:http-legacy:1.0' compile 'com.android.support:support-v4:25.2.0' compile 'com.github.manuelpeinado.fadingactionbar:fadingactionbar-abc:3.1.2' compile 'com.pnikosis:materialish-progress:1.7' compile 'me.drakeet.materialdialog:library:1.2.8' compile 'com.jakewharton:butterknife:7.0.1' compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' compile 'com.weiwangcn.betterspinner:library:1.1.0' compile 'com.nineoldandroids:library:2.4.0' compile 'com.android.support:design:25.2.0' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/Hankkin/Library/Android/sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # 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 *; #} -optimizationpasses 5 # 指定代码的压缩级别 -dontusemixedcaseclassnames # 是否使用大小写混合 -dontpreverify # 混淆时是否做预校验 -verbose # 混淆时是否记录日志 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 混淆时所采用的算法 -keep public class * extends android.app.Activity # 保持哪些类不被混淆 -keep public class * extends android.app.Application # 保持哪些类不被混淆 -keep public class * extends android.app.Service # 保持哪些类不被混淆 -keep public class * extends android.content.BroadcastReceiver # 保持哪些类不被混淆 -keep public class * extends android.content.ContentProvider # 保持哪些类不被混淆 -keep public class * extends android.app.backup.BackupAgentHelper # 保持哪些类不被混淆 -keep public class * extends android.preference.Preference # 保持哪些类不被混淆 -keep public class com.android.vending.licensing.ILicensingService # 保持哪些类不被混淆 -keepclasseswithmembernames class * { # 保持 native 方法不被混淆 native ; } -keepclasseswithmembers class * { # 保持自定义控件类不被混淆 public (android.content.Context, android.util.AttributeSet); } -keepclasseswithmembers class * {# 保持自定义控件类不被混淆 public (android.content.Context, android.util.AttributeSet, int); } -keepclassmembers class * extends android.app.Activity { # 保持自定义控件类不被混淆 public void *(android.view.View); } -keepclassmembers enum * { # 保持枚举 enum 类不被混淆 public static **[] values(); public static ** valueOf(java.lang.String); } -keep class * implements android.os.Parcelable { # 保持 Parcelable 不被混淆 public static final android.os.Parcelable$Creator *; } ================================================ FILE: app/src/androidTest/java/com/hankkin/compustrading/ApplicationTest.java ================================================ package com.hankkin.compustrading; import android.app.Application; import android.test.ApplicationTestCase; /** * Testing Fundamentals */ public class ApplicationTest extends ApplicationTestCase { public ApplicationTest() { super(Application.class); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/hankkin/compustrading/Application/MyApplication.java ================================================ package com.hankkin.compustrading.Application; import android.app.Application; import android.graphics.Bitmap; import android.os.Environment; import android.os.Handler; import com.hankkin.compustrading.R; import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator; import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.assist.QueueProcessingType; import com.nostra13.universalimageloader.core.decode.BaseImageDecoder; import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; import com.nostra13.universalimageloader.core.download.BaseImageDownloader; import java.io.File; import cn.bmob.v3.Bmob; /** * Created by Hankkin on 15/11/28. */ public class MyApplication extends Application { private static String DB_PATH = null; private MyApplication instance; @Override public void onCreate() { super.onCreate(); instance = this; //初始化Bmob Key Bmob.initialize(this,"b493c51d9d5c205dde89e4f4dedc10cd"); initImageloader(); } /** * 初始化imageloader * by Hankkin at:2015-12-23 19:29:57 */ public void initImageloader(){ DisplayImageOptions options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.ic_download) .showImageOnFail(R.drawable.ic_download) .resetViewBeforeLoading(false) // default .delayBeforeLoading(0) .cacheInMemory(true) // default .cacheOnDisk(true) // default .considerExifParams(true) // default .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default .bitmapConfig(Bitmap.Config.ARGB_8888) // default .displayer(new SimpleBitmapDisplayer()) // default .handler(new Handler()) // default .build(); // This configuration tuning is custom. You can tune every option, you may tune some of them, // or you can create default configuration by // ImageLoaderConfiguration.createDefault(this); // method. // File cacheDir = StorageUtils.getCacheDirectory(context); File file = new File(Environment.getExternalStorageDirectory().getPath() + "/compustrading"); if (!file.exists()) { file.mkdir(); } String path = file.getAbsolutePath() + "/files"; File fileSD = new File(path); if (!fileSD.exists()) { fileSD.mkdir(); } ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()) .memoryCacheExtraOptions(480, 800) // default = device screen dimensions .diskCacheExtraOptions(480, 800, null) .threadPoolSize(3) // default .threadPriority(Thread.NORM_PRIORITY - 1) // default .tasksProcessingOrder(QueueProcessingType.FIFO) // default .denyCacheImageMultipleSizesInMemory() .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) .memoryCacheSize(2 * 1024 * 1024) .memoryCacheSizePercentage(13) // default .diskCache(new UnlimitedDiskCache(file)) // default .diskCacheSize(50 * 1024 * 1024) .diskCacheFileCount(1000) .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default .imageDownloader(new BaseImageDownloader(getApplicationContext())) // default .imageDecoder(new BaseImageDecoder(true)) // default .defaultDisplayImageOptions(options) // default .writeDebugLogs() .build(); // Initialize ImageLoader with configuration. ImageLoader.getInstance().init(config); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/MainActivity.java ================================================ package com.hankkin.compustrading; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import com.hankkin.compustrading.activity.MainShowActivity; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; public class MainActivity extends AppCompatActivity { @Bind(R.id.btn_login) Button btnLogin; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init() { ButterKnife.bind(this); // Product product = new Product(0,"九成新Iphone6",4000,"") } @OnClick(R.id.btn_login) public void btnLoginClick() { Intent intent = new Intent(MainActivity.this, MainShowActivity.class); startActivity(intent); finish(); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/User/UserBean.java ================================================ package com.hankkin.myapplication.User; import cn.bmob.v3.BmobObject; /** * Created by Hankkin on 15/11/28. */ public class UserBean extends BmobObject { private String name; private String tel; private String icon_url; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTel() { return tel; } public void setTel(String tel) { this.tel = tel; } public String getIcon_url() { return icon_url; } public void setIcon_url(String icon_url) { this.icon_url = icon_url; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/Utils/BitmapUtils.java ================================================ package com.hankkin.compustrading.Utils; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.media.ExifInterface; import android.net.Uri; import android.os.Environment; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * Created by Hankkin on 15/12/24. */ public class BitmapUtils { public static Bitmap getCompressedBitmap(Activity act, String filepath) { Bitmap tempBitmap = null; float width = act.getResources().getDisplayMetrics().widthPixels; float height = act.getResources().getDisplayMetrics().heightPixels; try { if (width > 640) { tempBitmap = getSuitableBitmap(act, Uri.fromFile(new java.io.File(filepath)), 640, (640 / width) * height); } else { tempBitmap = getSuitableBitmap(act, Uri.fromFile(new java.io.File(filepath)), (int) width, (int) height); } } catch (FileNotFoundException e) { e.printStackTrace(); } return tempBitmap; } /** * 说明:请调用getSuitableBitmap()方法并传入图像路径,返回Bitmap *

* 修改宽高压缩比例 * by:Hankkin at:2015-2-14 */ public static Bitmap getSuitableBitmap(Activity act, Uri uri, float ww, float hh) throws FileNotFoundException { BitmapFactory.Options newOpts = new BitmapFactory.Options(); newOpts.inJustDecodeBounds = true;//只读边,不读内容 Bitmap bitmap = null; bitmap = BitmapFactory.decodeStream(act.getContentResolver().openInputStream(uri), null, newOpts); newOpts.inJustDecodeBounds = false; float w = newOpts.outWidth; float h = newOpts.outHeight; float wwh = 640f;// float hhh = (wwh / w) * h;// int be = 1; if (w > h && w > wwh) { be = (int) (newOpts.outWidth / wwh); } else if (w < h && h > hhh) { be = (int) (newOpts.outHeight / hhh); be += 1; } if (be <= 0) be = 1; newOpts.inSampleSize = be;//设置采样率 newOpts.inPreferredConfig = Bitmap.Config.ARGB_8888;//该模式是默认的,可不设 newOpts.inPurgeable = true;// 同时设置才会有效 newOpts.inInputShareable = true;//。当系统内存不够时候图片自动被回收 bitmap = BitmapFactory.decodeStream(act.getContentResolver().openInputStream(uri), null, newOpts); // return compressBmpFromBmp(bitmap);//原来的方法调用了这个方法企图进行二次压缩 //其实是无效的,大家尽管尝试 return bitmap; } /** * 质量压缩 * by:Hankkin at:2015-2-14 * * @param image * @return */ public static ByteArrayOutputStream compressImage(Bitmap image) { int options = 100; ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, options, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 while (baos.size() / 1024 > 30 && options > 40) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩 baos.reset(); options -= 10;//每次都减少10 image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中 } return baos; } /** * bitmap临时转为文件等待上传 * by Hankkin at:2015-4-24 * * @param bitmap * @param path * @return */ public static String saveBitmap(Bitmap bitmap, String path) { String filePath = null; String updatePath = Environment.getExternalStorageDirectory().getPath() + "/compustrading/tempUploadPic"; File fileSD = new File(updatePath); if (!fileSD.exists()) { fileSD.mkdir(); } try { filePath = updatePath + "/" + path; FileOutputStream out = new FileOutputStream(filePath); bitmap.compress(Bitmap.CompressFormat.JPEG, 30, out); out.flush(); out.close(); } catch (Exception e) { e.printStackTrace(); } return filePath.trim(); } /** * 读取照片exif信息中的旋转角度 * * @param path 照片路径 * @return角度 */ public static int readPictureDegree(String path) { int degree = 0; try { ExifInterface exifInterface = new ExifInterface(path); int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; break; } } catch (IOException e) { e.printStackTrace(); } return degree; } /** * 旋转照片 * by Hankkin at:2015年10月8日 11:17:04 * @param img * @return */ public static Bitmap toturn(Bitmap img){ Matrix matrix = new Matrix(); matrix.postRotate(+90); /*翻转90度*/ int width = img.getWidth(); int height =img.getHeight(); img = Bitmap.createBitmap(img, 0, 0, width, height, matrix, true); return img; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/Utils/HankkinUtils.java ================================================ /** * YUtils.java[V1.0.0] * classes : com.wadiankeji.creditsmanager.util.YUtils * * @author Hankkin Create at 2014年11月27日 下午7:48:31 */ package com.hankkin.compustrading.Utils; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.content.*; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.Rect; import android.media.ExifInterface; import android.media.MediaMetadataRetriever; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.Toast; import java.io.*; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * by 李斐 at 2014年11月27日 下午7:48:31 * 增加getPath相关方法 * by:李斐 at:2015-06-18 10:51:16 */ public class HankkinUtils { private static final String TAG = "YUtils"; // 记录屏幕的高度、宽度、密度等信息。 public static int screenH; public static int screenW; public static float screenDensity; // 屏幕密度(0.75 / 1.0 / 1.5) public static int screenDensityDpi; // 屏幕密度DPI(120 / 160 / 240) public static int statusBarHeight; // 状态栏高度 @SuppressLint("SimpleDateFormat") public static String longtimeToDate(long time) { Date now = new Date(time * 1000); SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss");// 可以方便地修改日期格式 dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8")); String dateStr = dateFormat.format(now); return dateStr; } /** * 修改时间戳转化时间 * by黄海杰 at:2015年7月27日 15:44:16 * * @param time * @return */ @SuppressLint("SimpleDateFormat") public static String longtimeToDayDate(long time) { SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm");// 可以方便地修改日期格式 String dateStr = dateFormat.format(new Date(time)); return dateStr; } public static String getCurrentTime(String format) { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault()); sdf.setTimeZone(TimeZone.getTimeZone("GMT+8")); String currentTime = sdf.format(date); return currentTime; } @SuppressLint("SimpleDateFormat") public static String longtimeToDateYMD(long time) { Date now = new Date(time); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");// 可以方便地修改日期格式 dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8")); String dateStr = dateFormat.format(now); return dateStr; } /** * 根据yyyy-MM-dd HH:mm:ss格式时间字符串转为long型时间戳 * * @param dateStr * @return date long * by:Hankkin at:2015年6月25日 17:38:25 修改时区设置 */ public static long stringDateToLong(String dateStr) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf.setTimeZone(TimeZone.getTimeZone("GMT+8")); Date date = null; try { date = sdf.parse(dateStr); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } return date.getTime(); } public static String getCurrentTime() { return getCurrentTime("yyyy-MM-dd HH:mm:ss"); } /** * 将时间戳转为代表"距现在多久之前"的字符串 * 修改为向下取整 * by黄海杰 at:2015年7月1日 17:08:55 * * @param dateTime 时间戳 * @return */ public static String getStandardDate(long dateTime) { StringBuffer sb = new StringBuffer(); // long t = Long.parseLong(timeStr); // long time = System.currentTimeMillis() - (t*1000); long time = System.currentTimeMillis() - (dateTime); long mill = (long) Math.floor(time / 1000);//秒前 long minute = (long) Math.floor(time / 60 / 1000.0f);// 分钟前 long hour = (long) Math.floor(time / 60 / 60 / 1000.0f);// 小时 long day = (long) Math.floor(time / 24 / 60 / 60 / 1000.0f);// 天前 if (day - 1 > 0) { sb.append(day + "天"); } else if (hour - 1 > 0) { if (hour >= 24) { sb.append("1天"); } else { sb.append(hour + "小时"); } } else if (minute - 1 > 0) { if (minute == 60) { sb.append("1小时"); } else { sb.append(minute + "分钟"); } } else if (mill - 1 > 0) { if (mill == 60) { sb.append("1分钟"); } else { sb.append(mill + "秒"); } } else { sb.append("刚刚"); } if (!sb.toString().equals("刚刚")) { sb.append("前"); } return sb.toString(); } /** * 根据时间戳的差获取时间差 * by黄海杰 at:2015年7月13日 11:24:25 * 修改超过一天的显示时间 * by黄海杰 at:2015年7月27日 15:44:32 * * @param dateTime * @return */ public static String getDateAgo(long dateTime) { String days = null; // long t = Long.parseLong(timeStr); // long time = System.currentTimeMillis() - (t*1000); long timeInterval = (System.currentTimeMillis() - (dateTime)) / 1000; if (timeInterval < 60) { days = "1分钟前"; } else if (timeInterval < 3600) { days = "" + (int) Math.round(timeInterval / 60) + "分钟内"; } else if (timeInterval < 86400) { if (timeInterval % 3600 > 1800) { days = "" + (int) Math.round((timeInterval / 3600) + 1) + "小时内"; } else { days = "" + (int) Math.round((timeInterval / 3600)) + "小时内"; } } // else if (timeInterval<2592000){ // days = ""+(int)Math.floor(timeInterval/86400)+"天前"; // } // else if (timeInterval<31536000){ // days = ""+(int)Math.floor(timeInterval/2592000)+"个月前"; // } // else { // days = ""+(int)Math.floor(timeInterval/31536000)+"年前"; // } else { days = longtimeToDayDate(dateTime); } return days; } /** * 升级检测 * * @param locVersionName * @param lastVersion * @return 是否升级 */ public static boolean checkUpdate(String locVersionName, String lastVersion) { boolean hasUpdate = false; String[] locVersionS = locVersionName.split("\\."); String[] lastVersionS = lastVersion.split("\\."); if (!locVersionName.equals(lastVersion)) { if (locVersionS != null && lastVersion != null) { int localLenth = locVersionS.length; int lastVerLenth = lastVersionS.length; // int netLenth = lastVersion.length(); for (int i = 0; i < lastVerLenth; i++) { if (localLenth < lastVerLenth && i == localLenth) { hasUpdate = true; return hasUpdate; } if (Integer.valueOf(lastVersionS[i]) > Integer .valueOf(locVersionS[i])) { hasUpdate = true; return hasUpdate; } else if (Integer.valueOf(lastVersionS[i]) < Integer .valueOf(locVersionS[i])) { hasUpdate = false; return hasUpdate; } } } } else { hasUpdate = false; } return hasUpdate; } /** * bitmap转byte数组 * * @param bmp * @param needRecycle * @return */ public static byte[] bmpToByteArray(final Bitmap bmp, final boolean needRecycle) { ByteArrayOutputStream output = new ByteArrayOutputStream(); bmp.compress(CompressFormat.PNG, 100, output); if (needRecycle) { bmp.recycle(); } byte[] result = output.toByteArray(); try { output.close(); } catch (Exception e) { e.printStackTrace(); } return result; } /** * 实现文本复制功能 add by lif * * @param content */ public static void copy(String content, Context context) { // 得到剪贴板管理器 ClipboardManager cmb = (ClipboardManager) context .getSystemService(Context.CLIPBOARD_SERVICE); cmb.setText(content.trim()); HankkinUtils.showToast(context, "内容已复制"); } /** * 实现粘贴功能 add by lif * * @param context * @return */ public static String paste(Context context) { // 得到剪贴板管理器 ClipboardManager cmb = (ClipboardManager) context .getSystemService(Context.CLIPBOARD_SERVICE); return cmb.getText().toString().trim(); } /** * 隐藏软键盘 */ public static void hideSoftInputMethod(Activity act) { View view = act.getWindow().peekDecorView(); if (view != null) { // 隐藏虚拟键盘 InputMethodManager inputmanger = (InputMethodManager) act .getSystemService(act.INPUT_METHOD_SERVICE); inputmanger.hideSoftInputFromWindow(view.getWindowToken(), 0); } } /** * 切换软件盘 显示隐藏 */ public static void switchSoftInputMethod(Activity act) { // 方法一(如果输入法在窗口上已经显示,则隐藏,反之则显示) InputMethodManager iMM = (InputMethodManager) act .getSystemService(Context.INPUT_METHOD_SERVICE); iMM.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); } /** * 验证是否手机号码 * * @param mobiles * @return */ public static boolean isMobileNO(String mobiles) { String telRegex = "[1][358]\\d{9}";//"[1]"代表第1位为数字1,"[358]"代表第二位可以为3、5、8中的一个,"\\d{9}"代表后面是可以是0~9的数字,有9位。 if (TextUtils.isEmpty(mobiles)) return false; else return mobiles.matches(telRegex); } /** * 中文识别 */ public static boolean hasChinese(String source) { String reg_charset = "([\\u4E00-\\u9FA5]*+)"; Pattern p = Pattern.compile(reg_charset); Matcher m = p.matcher(source); boolean hasChinese = false; while (m.find()) { if (!"".equals(m.group(1))) { hasChinese = true; } } return hasChinese; } /** * 用户名规则判断 * * @param uname * @return */ public static boolean isAccountStandard(String uname) { Pattern p = Pattern.compile("[A-Za-z0-9_]+"); Matcher m = p.matcher(uname); return m.matches(); } // java 合并两个byte数组 public static byte[] byteMerger(byte[] byte_1, byte[] byte_2) { byte[] byte_3 = new byte[byte_1.length + byte_2.length]; System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length); System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length); return byte_3; } /** * 删除文件夹下所有文件 * Hankkin at:2015年4月21日 20:05:01 * * @param file */ public static void delete(File file) { if (file.isFile()) { file.delete(); return; } if (file.isDirectory()) { File[] childFiles = file.listFiles(); if (childFiles == null || childFiles.length == 0) { file.delete(); return; } for (int i = 0; i < childFiles.length; i++) { delete(childFiles[i]); } // file.delete(); } } /** * 取得系统版本号 * by: Hankkin at: 2015-04-13 * * @param context * @return version 当前项目版本号 */ public static String GetVersion(Context context) { try { PackageInfo manager = context.getPackageManager().getPackageInfo( context.getPackageName(), 0); return manager.versionName; } catch (PackageManager.NameNotFoundException e) { return "Unknown"; } } public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri .getAuthority()); } public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri .getAuthority()); } public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri .getAuthority()); } public static boolean isGooglePhotosUri(Uri uri) { return "com.google.android.apps.photos.content".equals(uri .getAuthority()); } // public static String getDateAgo(int timeline){ // long curTimeline = System.currentTimeMillis(); // int timeInterval = timeline - curTimeline; // if (timeInterval<60){ // return "1分钟内"; // } // else if (timeInterval<3600){ // return ""+Math.floor(timeInterval/60)+"分钟前"; // } // else if (timeInterval<86400){ // return ""+Math.floor(timeInterval/3600)+"小时前"; // } // else if (timeInterval<2592000){ // return ""+Math.floor(timeInterval/86400)+"天前"; // } // else if (timeInterval<31536000){ // return ""+Math.floor(timeInterval/2592000)+"个月前"; // } // else { // return ""+Math.floor(timeInterval/31536000)+"年前"; // } // } /** * 创建视频临时帧图片 * by Hankkin at:2015年8月18日 11:21:03 * * @param filePath * @return */ public static Bitmap getVideoThumbnail(String filePath) { Bitmap bitmap = null; MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { retriever.setDataSource(filePath); bitmap = retriever.getFrameAtTime(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (RuntimeException e) { e.printStackTrace(); } finally { try { retriever.release(); } catch (RuntimeException e) { e.printStackTrace(); } } return bitmap; } public static boolean isDebug = true;// 是否需要打印bug,可以在application的onCreate函数里面初始化 /** * 记录上次点击时间 */ private static long lastClickTime; /** * 是否快速双击点击 * * @return isFastDoubleClick */ public static boolean isFastDoubleClick() { long time = System.currentTimeMillis(); if (time - lastClickTime < 600) { return true; } else { lastClickTime = time; return false; } } /*-----------------------toast start-----------------------*/ /** * 提示字符串 * short Toast * * @param context * @param text */ public static void showToast(Context context, String text) { Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } /** * 提示字符串 * short Toast * * @param context * @param text */ public static void showLToast(Context context, String text) { Toast toast = Toast.makeText(context, text, Toast.LENGTH_LONG); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } /** * 提示根据ResId关联字符串 * short Toast * by:Hankkin at:2015年4月30日 14:39:41 * * @param context * @param resId */ public static void showToast(Context context, int resId) { Toast toast = Toast.makeText(context, resId, Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } /** * 提示根据ResId关联字符串 * 时常long Toast * by:Hankkin at:2015年4月30日 14:39:41 * * @param context * @param resId */ public static void showLToast(Context context, int resId) { Toast toast = Toast.makeText(context, resId, Toast.LENGTH_LONG); ; toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); } /*-----------------------toast end-----------------------*/ /*获得屏幕相关的辅助类*/ /*-----------------------screen start-----------------------*/ /** * 获得屏幕高度 * * @param context * @return */ public static int getScreenWidth(Context context) { WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); return outMetrics.widthPixels; } /** * 获得屏幕宽度 * * @param context * @return */ public static int getScreenHeight(Context context) { WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); return outMetrics.heightPixels; } /** * 获取屏幕密度 * * @param context * @return */ public static float getScreenDensity(Context context) { return context.getResources().getDisplayMetrics().density; } /** * dip转px像素 * * @param context * @param px * @return */ public static int dip2px(Context context, float px) { final float scale = getScreenDensity(context); return (int) (px * scale + 0.5); } /** * 获得状态栏的高度 * * @param context * @return */ public static int getStatusHeight(Context context) { int statusHeight = -1; try { Class clazz = Class.forName("com.android.internal.R$dimen"); Object object = clazz.newInstance(); int height = Integer.parseInt(clazz.getField("status_bar_height") .get(object).toString()); statusHeight = context.getResources().getDimensionPixelSize(height); } catch (Exception e) { e.printStackTrace(); } return statusHeight; } /** * 获取当前屏幕截图,包含状态栏 * * @param activity * @return */ public static Bitmap snapShotWithStatusBar(Activity activity) { View view = activity.getWindow().getDecorView(); view.setDrawingCacheEnabled(true); view.buildDrawingCache(); Bitmap bmp = view.getDrawingCache(); int width = getScreenWidth(activity); int height = getScreenHeight(activity); Bitmap bp = null; bp = Bitmap.createBitmap(bmp, 0, 0, width, height); view.destroyDrawingCache(); return bp; } /** * 获取当前屏幕截图,不包含状态栏 * * @param activity * @return */ public static Bitmap snapShotWithoutStatusBar(Activity activity) { View view = activity.getWindow().getDecorView(); view.setDrawingCacheEnabled(true); view.buildDrawingCache(); Bitmap bmp = view.getDrawingCache(); Rect frame = new Rect(); activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); int statusBarHeight = frame.top; int width = getScreenWidth(activity); int height = getScreenHeight(activity); Bitmap bp = null; bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height - statusBarHeight); view.destroyDrawingCache(); return bp; } /** * 根据手机的分辨率从 px(像素) 的单位 转成为 dp */ public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } /** * 读取照片exif信息中的旋转角度 * * @param path 照片路径 * @return角度 */ public static int readPictureDegree(String path) { int degree = 0; try { ExifInterface exifInterface = new ExifInterface(path); int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; break; } } catch (IOException e) { e.printStackTrace(); } return degree; } /*-----------------------screen end-----------------------*/ /*日志打印*/ /*-----------------------log start-----------------------*/ // 下面四个是默认tag的函数 public static void i(String msg) { if (isDebug) Log.i(TAG, msg); } public static void d(String msg) { if (isDebug) Log.d(TAG, msg); } public static void e(String msg) { if (isDebug) Log.e(TAG, msg); } public static void v(String msg) { if (isDebug) Log.v(TAG, msg); } // 下面是传入自定义tag的函数 public static void i(String tag, String msg) { if (isDebug) Log.i(tag, msg); } public static void d(String tag, String msg) { if (isDebug) Log.i(tag, msg); } public static void e(String tag, String msg) { if (isDebug) Log.i(tag, msg); } public static void v(String tag, String msg) { if (isDebug) Log.i(tag, msg); } /*-----------------------log end-----------------------*/ /** * 字符串转MD5 * by黄海杰 at:2015-10-29 16:15:32 * * @param string * @return */ public static String md5(String string) { byte[] hash; try { hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8")); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Huh, MD5 should be supported?", e); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Huh, UTF-8 should be supported?", e); } StringBuilder hex = new StringBuilder(hash.length * 2); for (byte b : hash) { if ((b & 0xFF) < 0x10) hex.append("0"); hex.append(Integer.toHexString(b & 0xFF)); } return hex.toString(); } /** * 获取手机号 * by Hankkin * @param context * @return */ public static String getPhoneNumber(Context context){ TelephonyManager mTelephonyMgr; mTelephonyMgr = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); return mTelephonyMgr.getLine1Number(); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/activity/BaseActivity.java ================================================ package com.hankkin.compustrading.activity; import android.annotation.TargetApi; import android.app.Activity; import android.content.ContentUris; import android.content.Context; import android.content.CursorLoader; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.PersistableBundle; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import com.hankkin.compustrading.FileUploadListener; import com.hankkin.compustrading.R; import com.hankkin.compustrading.Utils.HankkinUtils; import com.hankkin.compustrading.model.Person; import com.pnikosis.materialishprogress.ProgressWheel; import java.io.File; import cn.bmob.v3.datatype.BmobFile; import cn.bmob.v3.exception.BmobException; import cn.bmob.v3.listener.UploadFileListener; import me.drakeet.materialdialog.MaterialDialog; /** * Created by Hankkin on 15/12/20. */ public class BaseActivity extends AppCompatActivity { /*请求相机Code*/ public static final int REQUST_CODE_CAMERA = 0; /*请求相册Code*/ public static final int REQUEST_CODE_GALLERY = 1; /*发布商品Code*/ public static final int REQUEST_CODE_FABU = 2; public MaterialDialog loadDialog; @Override public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) { super.onCreate(savedInstanceState, persistentState); } public Person getCurrentPerson(Context context){ Person person = Person.getCurrentUser(Person.class); return person; } /** * 调用相册 * by Hankkin at:2015-12-20 23:00:16 * @param act */ public void getImageFromGallery(Activity act) { Intent intent = new Intent(Intent.ACTION_PICK); intent.setType("image/*"); // 开启一个带有返回值的Activity,请求码为PHOTO_REQUEST_GALLERY act.startActivityForResult(intent, REQUEST_CODE_GALLERY); } /** * 上传图片 * by Hankkin at:2015-12-20 23:00:34 */ public void uploadImg(final String filepath, Context context, final FileUploadListener listener){ final BmobFile file = new BmobFile(new File(filepath)); file.upload(new UploadFileListener() { @Override public void done(BmobException e) { if (e == null){ listener.success(file.getFileUrl()); } else { listener.fail(); } } }); } public void showLoadingDialog(){ loadDialog = new MaterialDialog(this); View view = LayoutInflater.from(this).inflate(R.layout.loading,null,false); ProgressWheel wheel = (ProgressWheel) view.findViewById(R.id.pw_loading); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(80,80); params.height = HankkinUtils.dip2px(this,80); params.width = HankkinUtils.dip2px(this,80); wheel.setLayoutParams(params); wheel.setBackgroundColor(getResources().getColor(R.color.light_white)); loadDialog.setView(view); loadDialog.setBackgroundResource(getResources().getColor(R.color.transparent)); loadDialog.show(); } public void dimissDialog(){ if (loadDialog!=null){ loadDialog.dismiss(); } } /** * 初始化图片路径 * * @return */ public static String iniFilePath(Activity act) { String filepath = null; String path = null; File fileSD = null; // 准备存储位置 boolean sdExist = Environment.getExternalStorageState() .equals(android.os.Environment.MEDIA_MOUNTED); if (!sdExist) { HankkinUtils.showLToast(act, "没有找到SD存储卡"); return null; } else { //TODO 内容提示完善 path = Environment.getExternalStorageDirectory().getPath() + "/compustrading/Camera"; fileSD = new File(path); if (fileSD.exists()) { filepath = path + "/" + System.currentTimeMillis() + ".jpg"; } else { fileSD.mkdir(); filepath = fileSD.getAbsolutePath() + "/" + System.currentTimeMillis() + ".jpg"; } return filepath; } } /** * 调用相机 * * @param act */ public static void goCamera(Activity act, String filepath) { File file = new File(filepath); // 启动Camera Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); act.startActivityForResult(intent, REQUST_CODE_CAMERA); } /** * 从URI获取本地路径 * * @param selectedVideoUri * @param contentResolver * @return */ public static String getAbsoluteImagePath(Activity activity, Uri contentUri) { //如果是对媒体文件,在android开机的时候回去扫描,然后把路径添加到数据库中。 //由打印的contentUri可以看到:2种结构。正常的是:content://那么这种就要去数据库读取path。 //另外一种是Uri是 file:///那么这种是 Uri.fromFile(File file);得到的 System.out.println(contentUri); String[] projection = { MediaStore.Images.Media.DATA }; String urlpath; CursorLoader loader = new CursorLoader(activity,contentUri, projection, null, null, null); Cursor cursor = loader.loadInBackground(); try { int column_index = cursor.getColumnIndex(MediaStore.Images.Media.DATA); cursor.moveToFirst(); urlpath =cursor.getString(column_index); //如果是正常的查询到数据库。然后返回结构 return urlpath; } catch (Exception e) { e.printStackTrace(); // TODO: handle exception }finally{ if(cursor != null){ cursor.close(); } } //如果是文件。Uri.fromFile(File file)生成的uri。那么下面这个方法可以得到结果 urlpath = contentUri.getPath(); return urlpath; } /** * android系统版本选择图库图片解决方法---获取图片路径 * by Hankkin at:2015-3-10 * * @param context * @param uri * @return */ @TargetApi(Build.VERSION_CODES.KITKAT) public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // TODO handle non-primary volumes } // DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = MediaStore.MediaColumns._ID + "=?"; final String[] selectionArgs = new String[]{split[1]}; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { // Return the remote address if (isGooglePhotosUri(uri)) return uri.getLastPathSegment(); return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } /** * android系统版本选择图库图片解决方法--获取数据 * by Hankkin at:2015-3-10 * * @param context * @param uri * @param selection * @param selectionArgs * @return */ public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = MediaStore.MediaColumns.DATA; final String[] projection = {column}; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int index = cursor.getColumnIndexOrThrow(column); return cursor.getString(index); } } finally { if (cursor != null) cursor.close(); } return null; } public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri .getAuthority()); } public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri .getAuthority()); } public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri .getAuthority()); } public static boolean isGooglePhotosUri(Uri uri) { return "com.google.android.apps.photos.content".equals(uri .getAuthority()); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/activity/LoginActivity.java ================================================ package com.hankkin.compustrading.activity; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import com.hankkin.compustrading.R; import com.hankkin.compustrading.Utils.HankkinUtils; import com.hankkin.compustrading.model.Person; import com.hankkin.compustrading.sharepreference.MySP; import com.pnikosis.materialishprogress.ProgressWheel; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; import cn.bmob.v3.exception.BmobException; import cn.bmob.v3.listener.LogInListener; public class LoginActivity extends AppCompatActivity { @Bind(R.id.btn_login) Button btnLogin; @Bind(R.id.tv_back) TextView tvBack; @Bind(R.id.tv_register) TextView tvRegister; @Bind(R.id.et_login_name) EditText etName; @Bind(R.id.et_login_pwd) EditText etPwd; @Bind(R.id.pw_loading) ProgressWheel wheel; public static LoginActivity instance; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); instance = this; setContentView(R.layout.activity_login); ButterKnife.bind(this); initViews(); } private void initViews() { wheel.stopSpinning(); } @OnClick(R.id.tv_register) public void register() { Intent intent = new Intent(LoginActivity.this, RegisterActivity.class); startActivity(intent); } @OnClick(R.id.tv_back) public void back() { finish(); } @OnClick(R.id.btn_login) public void login() { wheel.spin(); final String name = etName.getText().toString().trim(); final String pwd = etPwd.getText().toString().trim(); if (TextUtils.isEmpty(name)) { HankkinUtils.showToast(LoginActivity.this, "用户名不能为空"); return; } if (TextUtils.isEmpty(pwd)) { HankkinUtils.showToast(LoginActivity.this, "密码不能为空"); return; } Person.loginByAccount(name, pwd, new LogInListener() { @Override public void done(Person person, BmobException e) { MySP.setPASSWoRD(LoginActivity.this, pwd); MySP.setUSERNAME(LoginActivity.this, name); wheel.stopSpinning(); HankkinUtils.showToast(LoginActivity.this, "登录成功"); Intent intent = new Intent(LoginActivity.this, MainShowActivity.class); startActivity(intent); finish(); if (LoginActivity.instance != null) { LoginActivity.instance.finish(); } if (PersonActivity.instance != null) { PersonActivity.instance.finish(); } if (MainShowActivity.instance!=null){ MainShowActivity.instance.finish(); } } }); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/activity/MainShowActivity.java ================================================ package com.hankkin.compustrading.activity; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.view.ViewPager; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.text.TextUtils; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; import com.hankkin.compustrading.R; import com.hankkin.compustrading.Utils.HankkinUtils; import com.hankkin.compustrading.adapter.CategoryFragmentAdapter; import com.hankkin.compustrading.fragment.CateDetailFragment; import com.hankkin.compustrading.model.Category; import com.hankkin.compustrading.model.Person; import com.hankkin.compustrading.model.Product; import com.hankkin.compustrading.view.PagerSlidingTabStrip; import com.hankkin.compustrading.view.RippleView; import com.hankkin.compustrading.view.RoundedImageView; import com.hankkin.compustrading.view.floatbutton.FloatingActionButton; import com.hankkin.compustrading.view.floatbutton.FloatingActionsMenu; import com.nostra13.universalimageloader.core.ImageLoader; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import butterknife.Bind; import butterknife.ButterKnife; import cn.bmob.v3.BmobQuery; import cn.bmob.v3.BmobUser; import cn.bmob.v3.exception.BmobException; import cn.bmob.v3.listener.FindListener; import cn.bmob.v3.update.BmobUpdateAgent; public class MainShowActivity extends BaseActivity { /** * 分类滑动选项卡 */ private PagerSlidingTabStrip pagerTab; /** * 滑动组件 */ private ViewPager pager; /** * 选项fragment界面 */ private ArrayList fragments; /** * 分类数组 */ private ArrayList categories = new ArrayList<>(); /** * 分类界面适配器 */ private CategoryFragmentAdapter adapter; DrawerLayout drawerLayout; ActionBarDrawerToggle drawerToggle; @Bind(R.id.rv_usericon) RoundedImageView rvUser; @Bind(R.id.tv_person) TextView tvPerson; @Bind(R.id.rv_logreg) RippleView rvLogReg; @Bind(R.id.tv_username) TextView tvName; @Bind(R.id.rv_buy) RippleView rvBuy; @Bind(R.id.rv_sale) RippleView rvSale; @Bind(R.id.rv_sina) RippleView rvSina; @Bind(R.id.rv_qq) RippleView rvQQ; @Bind(R.id.tv_qq) TextView tvQQ; @Bind(R.id.tv_sina) TextView tvSina; @Bind(R.id.tv_buy) TextView tvBuy; @Bind(R.id.tv_sale) TextView tvSale; @Bind(R.id.tv_show) TextView tvShow; private Handler handler; private Person person; @Bind(R.id.multiple_actions) FloatingActionsMenu floatingActionsMenu; public static MainShowActivity instance; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); instance = this; setContentView(R.layout.activity_main_show); BmobUpdateAgent.update(this); ButterKnife.bind(this); init(); handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 0) { for (int i = 0; i < categories.size(); i++) { CateDetailFragment fragment = new CateDetailFragment(); fragment.setFab(floatingActionsMenu); Bundle bundle = new Bundle(); bundle.putInt("cid", categories.get(i).getId()); bundle.putSerializable("products", (Serializable) msg.obj); fragment.setArguments(bundle); fragments.add(fragment); } adapter = new CategoryFragmentAdapter(getSupportFragmentManager(), fragments, categories); pager.setAdapter(adapter); pagerTab.setViewPager(pager); dimissDialog(); } } }; } /** * 初始化数据 * by Hankkin at:2015-11-29 19:29:52 */ private void init() { drawerLayout = (DrawerLayout) findViewById(R.id.drawerlayout); drawerToggle = new ActionBarDrawerToggle(MainShowActivity.this, drawerLayout, R.string.hello_world, R.string.hello_world); drawerLayout.setDrawerListener(drawerToggle); //设定左上角突变可点击 getSupportActionBar().setHomeButtonEnabled(true); // 给左上角图标的左边加上一个返回的图标 。对应ActionBar.DISPLAY_HOME_AS_UP getSupportActionBar().setDisplayHomeAsUpEnabled(true); //设置标题 getSupportActionBar().setTitle(getResources().getString(R.string.action_title)); pager = (ViewPager) findViewById(R.id.pager); pagerTab = (PagerSlidingTabStrip) findViewById(R.id.tab); pager.setOffscreenPageLimit(4); fragments = new ArrayList<>(); /** * 更新按钮点击事件 * by Hankkin at:2015-12-23 17:29:52 */ FloatingActionButton fbUpdate = (FloatingActionButton) findViewById(R.id.fb_update); fbUpdate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { fragments.get(pager.getCurrentItem()).hideFab(); fragments.get(pager.getCurrentItem()).updatePeo(); } }); /** * 新建按钮点击事件 * by Hankkin at:2015-12-23 17:30:17 */ FloatingActionButton fbWrite = (FloatingActionButton) findViewById(R.id.fb_new); fbWrite.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Person person = BmobUser.getCurrentUser(Person.class); if (person != null) { fragments.get(pager.getCurrentItem()).hideFab(); Intent intent = new Intent(MainShowActivity.this, NewProductActivity.class); startActivity(intent); } else { fragments.get(pager.getCurrentItem()).hideFab(); Intent intent = new Intent(MainShowActivity.this, LoginActivity.class); startActivity(intent); HankkinUtils.showToast(MainShowActivity.this, "请先登录"); } } }); FloatingActionButton fbMy = (FloatingActionButton) findViewById(R.id.fb_person); fbMy.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { fragments.get(pager.getCurrentItem()).hideFab(); Intent intent = new Intent(MainShowActivity.this, PersonActivity.class); startActivity(intent); } }); person = getCurrentPerson(MainShowActivity.this); if (person != null) { rvBuy.setVisibility(View.VISIBLE); rvSale.setVisibility(View.VISIBLE); rvQQ.setVisibility(View.GONE); rvSina.setVisibility(View.GONE); if (!TextUtils.isEmpty(person.getUser_icon())) { ImageLoader.getInstance().displayImage(person.getUser_icon(), rvUser); } tvPerson.setText("个人中心"); if (!TextUtils.isEmpty(person.getNickname())) { tvName.setText(person.getNickname()); } else { tvName.setText("用户" + person.getUsername().substring(0, 3)); } tvShow.setText("我的"); } else { rvUser.setBackground(getResources().getDrawable(R.drawable.defaut)); tvPerson.setText("登录或注册"); tvName.setText(""); rvBuy.setVisibility(View.GONE); rvSale.setVisibility(View.GONE); rvQQ.setVisibility(View.VISIBLE); rvSina.setVisibility(View.VISIBLE); tvShow.setText("其他登录方式"); } rvLogReg.setOnRippleCompleteListener(new RippleView.OnRippleCompleteListener() { @Override public void onComplete(RippleView rippleView) { if (person != null) { Intent intent = new Intent(MainShowActivity.this, PersonActivity.class); startActivity(intent); } else { Intent intent = new Intent(MainShowActivity.this, LoginActivity.class); startActivity(intent); } } }); rvBuy.setOnRippleCompleteListener(new RippleView.OnRippleCompleteListener() { @Override public void onComplete(RippleView rippleView) { drawerLayout.closeDrawers(); } }); rvSale.setOnRippleCompleteListener(new RippleView.OnRippleCompleteListener() { @Override public void onComplete(RippleView rippleView) { Intent intent = new Intent(MainShowActivity.this, NewProductActivity.class); startActivity(intent); drawerLayout.closeDrawers(); } }); ArrayList categoryList = new ArrayList<>(); categoryList = (ArrayList) getIntent().getSerializableExtra("categories"); if (categoryList!=null){ if (categoryList.size()>0){ categories.addAll(categoryList); queryProductsHttp(); } else { queryCategory(); } } else { queryCategory(); } } private void queryProductsHttp() { BmobQuery productBmobQuery = new BmobQuery<>(); productBmobQuery.order("-createdAt"); productBmobQuery.findObjects(new FindListener() { @Override public void done(List list, BmobException e) { if (e == null){ if (list != null && list.size() > 0) { List data = new ArrayList(); for (Product p : list) { data.add(p); } Message msg = new Message(); msg.what = 0; msg.obj = data; handler.sendMessage(msg); } } } }); } @Override public void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); //该方法会自动和actionBar关联, 将开关的图片显示在了action上,如果不设置,也可以有抽屉的效果,不过是默认的图标 drawerToggle.syncState(); } /** 设备配置改变时 */ @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); drawerToggle.onConfigurationChanged(newConfig); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (drawerToggle.onOptionsItemSelected(item)) return true; // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_person) { Intent intent = new Intent(MainShowActivity.this, PersonActivity.class); startActivity(intent); } return super.onOptionsItemSelected(item); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (floatingActionsMenu.isExpanded()) { floatingActionsMenu.collapse(); return true; } } return super.onKeyDown(keyCode, event); } /** * Bmob查询分类数据显示选项卡 * by Hankkin at:2015-11-29 19:39:45 */ private void queryCategory() { showLoadingDialog(); BmobQuery categoryBmobQuery = new BmobQuery<>(); categoryBmobQuery.order("createdAt");// 按照时间降序 categoryBmobQuery.findObjects(new FindListener() { @Override public void done(List list, BmobException e) { if (e == null){ if (list != null && list.size() > 0) { categories = new ArrayList(); categories.addAll(list); queryProductsHttp(); } } else { HankkinUtils.showToast(MainShowActivity.this, e.getMessage()); } } }); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/activity/NewProductActivity.java ================================================ package com.hankkin.compustrading.activity; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.hankkin.compustrading.FileUploadListener; import com.hankkin.compustrading.R; import com.hankkin.compustrading.Utils.BitmapUtils; import com.hankkin.compustrading.Utils.HankkinUtils; import com.hankkin.compustrading.model.Product; import com.weiwangcn.betterspinner.library.BetterSpinner; import java.util.Date; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; import cn.bmob.v3.exception.BmobException; import cn.bmob.v3.listener.SaveListener; import me.drakeet.materialdialog.MaterialDialog; public class NewProductActivity extends BaseActivity { @Bind(R.id.et_tel) EditText etTel; @Bind(R.id.et_name) EditText etName; @Bind(R.id.et_price) EditText etPrice; @Bind(R.id.spinner_school) BetterSpinner spinnerSchool; @Bind(R.id.tv_cate) TextView tvCate; @Bind(R.id.btn_fabu) Button btnFabu; @Bind(R.id.iv_add_pro) ImageView ivAddPro; @Bind(R.id.tv_back) TextView tvBack; private String[] schools; private String filePath = ""; private int cid=0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_new_product); ButterKnife.bind(this); init(); } private void init(){ schools = getResources().getStringArray(R.array.school); ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_dropdown_item_1line, schools); spinnerSchool.setAdapter(adapter); // String tel = HankkinUtils.getPhoneNumber(NewProductActivity.this); // if (!TextUtils.isEmpty(tel)){ // etTel.setText(tel); // } tvBack.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); } /** * 选择分类对话框 * by Hankkin at:2015-12-24 17:39:17 */ @OnClick(R.id.tv_cate) void showCate(){ final ArrayAdapter arrayAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1); final String[] cates = getResources().getStringArray(R.array.cate); for (int j = 0; j < cates.length; j++) { arrayAdapter.add(cates[j]); } ListView listView = new ListView(this); listView.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); float scale = getResources().getDisplayMetrics().density; listView.setDividerHeight(1); listView.setAdapter(arrayAdapter); final MaterialDialog alert = new MaterialDialog(this).setTitle( "请选择分类").setContentView(listView); alert.show(); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { String cate = cates[position]; tvCate.setText(cate); cid = position; alert.dismiss(); } }); } /** * 选择图片对话框 * by Hankkin at:2015-12-20 23:25:37 */ @OnClick(R.id.iv_add_pro) void showMDdialog() { View view = LayoutInflater.from(NewProductActivity.this).inflate(R.layout.view_select_img, null, false); final MaterialDialog dialog = new MaterialDialog(this).setView(view); dialog.show(); TextView tvGallery = (TextView) view.findViewById(R.id.tv_gallery); tvGallery.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); getImageFromGallery(NewProductActivity.this); } }); TextView tvCamera = (TextView) view.findViewById(R.id.tv_camera); tvCamera.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); filePath = iniFilePath(NewProductActivity.this); goCamera(NewProductActivity.this, filePath); } }); } /** * 接受选择照片的结果显示 * by Hankkin at:2015-12-24 17:41:49 * @param requestCode * @param resultCode * @param data */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_GALLERY) { if(data == null){ return; } filePath = getAbsoluteImagePath(NewProductActivity.this, data.getData()); if (!TextUtils.isEmpty(filePath)) { ivAddPro.setImageBitmap(BitmapUtils.getCompressedBitmap(NewProductActivity.this, filePath)); } } else if (requestCode == REQUST_CODE_CAMERA) { if (!TextUtils.isEmpty(filePath)) { Bitmap temp = BitmapUtils.getCompressedBitmap(NewProductActivity.this, filePath); if(temp==null){ return; } ivAddPro.setImageBitmap(temp); } } } /** * 发布按钮点击事件 * 上传图片, 创建新商品 * by Hankkin at:2015-12-24 18:33:15 */ @OnClick(R.id.btn_fabu) void addNewPro(){ if (!HankkinUtils.isMobileNO(etTel.getText().toString().trim())){ HankkinUtils.showToast(NewProductActivity.this,"请输入正确的手机号"); return; } if (TextUtils.isEmpty(etPrice.getText().toString().trim())|| TextUtils.isEmpty(etTel.getText().toString().trim())|| TextUtils.isEmpty(etName.getText().toString().trim())){ HankkinUtils.showToast(NewProductActivity.this,"请完善信息"); return; } if (TextUtils.isEmpty(tvCate.getText())||TextUtils.isEmpty(spinnerSchool.getText().toString())){ HankkinUtils.showToast(NewProductActivity.this,"请完善信息"); return; } if (!TextUtils.isEmpty(filePath)){ showLoadingDialog(); Bitmap tempBitmap = BitmapUtils.getCompressedBitmap(NewProductActivity.this, filePath); if (BitmapUtils.readPictureDegree(filePath) == 90) { tempBitmap = BitmapUtils.toturn(tempBitmap); } filePath = BitmapUtils.saveBitmap(tempBitmap,new Date().getTime() + ""); uploadImg(filePath, NewProductActivity.this, new FileUploadListener() { @Override public void success(String url) { Product product = new Product(); product.setName(etName.getText().toString()); product.setCid(cid); product.setUsername(getCurrentPerson(NewProductActivity.this).getNickname()); product.setPrice(etPrice.getText().toString()); product.setSchool(spinnerSchool.getText().toString()); product.setProduct_url(url); product.setUser_tel(etTel.getText().toString()); product.setUser_icon_url(getCurrentPerson(NewProductActivity.this).getUser_icon()); addProHttp(product); } @Override public void fail() { dimissDialog(); HankkinUtils.showToast(NewProductActivity.this, "发布失败"); } }); } } /** * 保存新商品 * by Hankkin at:2015-12-24 18:32:38 * @param product */ private void addProHttp(final Product product){ product.save(new SaveListener() { @Override public void done(String s, BmobException e) { if (e == null){ dimissDialog(); HankkinUtils.showToast(NewProductActivity.this, "发布成功"); finish(); } else { dimissDialog(); HankkinUtils.showToast(NewProductActivity.this,"发布失败"); } } }); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/activity/PersonActivity.java ================================================ package com.hankkin.compustrading.activity; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.text.TextUtils; import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import com.hankkin.compustrading.FileUploadListener; import com.hankkin.compustrading.R; import com.hankkin.compustrading.Utils.BitmapUtils; import com.hankkin.compustrading.Utils.HankkinUtils; import com.hankkin.compustrading.model.Person; import com.hankkin.compustrading.model.PersonShow; import com.hankkin.compustrading.view.PullToZoomScrollViewEx; import com.hankkin.compustrading.view.RippleView; import com.hankkin.compustrading.view.RoundedImageView; import com.nostra13.universalimageloader.core.ImageLoader; import java.util.ArrayList; import java.util.Date; import java.util.List; import butterknife.Bind; import cn.bmob.v3.exception.BmobException; import cn.bmob.v3.listener.UpdateListener; import me.drakeet.materialdialog.MaterialDialog; public class PersonActivity extends BaseActivity { @Bind(R.id.tv_back) TextView tvBack; private PullToZoomScrollViewEx scrollView; private List data = new ArrayList<>(); private TextView tvLogin, tvRegister; private RoundedImageView ivUserIcon; View headView; View zoomView; View contentView; public static PersonActivity instance; private String filePath = ""; private RippleView rvLogout; private RippleView rvBuy; private RippleView rvSale; private TextView tvLogout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); instance = this; setContentView(R.layout.activity_person); initViews(); initData(); } private void initViews() { tvBack = (TextView) findViewById(R.id.tv_back); scrollView = (PullToZoomScrollViewEx) findViewById(R.id.my_pull_scoll); loadViewForCode(); scrollView.getPullRootView().findViewById(R.id.tv_test1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); DisplayMetrics localDisplayMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics); int mScreenHeight = localDisplayMetrics.heightPixels; int mScreenWidth = localDisplayMetrics.widthPixels; LinearLayout.LayoutParams localObject = new LinearLayout.LayoutParams(mScreenWidth, (int) (9.0F * (mScreenWidth / 16.0F))); scrollView.setHeaderLayoutParams(localObject); tvBack.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); } private void initData() { Person person = Person.getCurrentUser(Person.class); if (person != null) { ImageLoader.getInstance().displayImage(person.getUser_icon(), ivUserIcon); tvLogin.setVisibility(View.GONE); tvRegister.setVisibility(View.GONE); tvLogout.setText("退出账号"); } else { tvLogin.setVisibility(View.VISIBLE); tvRegister.setVisibility(View.VISIBLE); ivUserIcon.setImageDrawable(getResources().getDrawable(R.drawable.defaut)); tvLogout.setText("登录"); } } private void loadViewForCode() { PullToZoomScrollViewEx scrollView = (PullToZoomScrollViewEx) findViewById(R.id.my_pull_scoll); headView = LayoutInflater.from(this).inflate(R.layout.profile_head_view, null, false); zoomView = LayoutInflater.from(this).inflate(R.layout.profile_zoom_view, null, false); contentView = LayoutInflater.from(this).inflate(R.layout.profile_contect_view, null, false); scrollView.setHeaderView(headView); scrollView.setZoomView(zoomView); scrollView.setScrollContentView(contentView); tvLogin = (TextView) headView.findViewById(R.id.tv_login); tvRegister = (TextView) headView.findViewById(R.id.tv_register); ivUserIcon = (RoundedImageView) headView.findViewById(R.id.iv_user_head); rvLogout = (RippleView) contentView.findViewById(R.id.rv_logout); tvLogout = (TextView) contentView.findViewById(R.id.tv_logout); rvLogout.setOnRippleCompleteListener(new RippleView.OnRippleCompleteListener() { @Override public void onComplete(RippleView rippleView) { Person.logOut(); HankkinUtils.showToast(PersonActivity.this, "已注销"); initData(); finish(); } }); rvBuy = (RippleView) contentView.findViewById(R.id.rv_buy); rvBuy.setOnRippleCompleteListener(new RippleView.OnRippleCompleteListener() { @Override public void onComplete(RippleView rippleView) { finish(); } }); rvSale = (RippleView) contentView.findViewById(R.id.rv_sale); rvSale.setOnRippleCompleteListener(new RippleView.OnRippleCompleteListener() { @Override public void onComplete(RippleView rippleView) { Intent intent = new Intent(PersonActivity.this,NewProductActivity.class); startActivity(intent); finish(); } }); tvLogin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(PersonActivity.this, LoginActivity.class); startActivity(intent); } }); tvRegister.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(PersonActivity.this, RegisterActivity.class); startActivity(intent); } }); ivUserIcon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (getCurrentPerson(PersonActivity.this)!=null){ showMDdialog(); } } }); } /** * 选择图片对话框 * by Hankkin at:2015-12-20 23:25:37 */ private void showMDdialog() { View view = LayoutInflater.from(PersonActivity.this).inflate(R.layout.view_select_img, null, false); final MaterialDialog dialog = new MaterialDialog(this).setView(view); dialog.show(); TextView tvGallery = (TextView) view.findViewById(R.id.tv_gallery); tvGallery.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); getImageFromGallery(PersonActivity.this); } }); TextView tvCamera = (TextView) view.findViewById(R.id.tv_camera); tvCamera.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); filePath = iniFilePath(PersonActivity.this); goCamera(PersonActivity.this, filePath); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_GALLERY) { showLoadingDialog(); filePath = getPath(PersonActivity.this, data.getData()); if (!TextUtils.isEmpty(filePath)) { uploadImg(filePath, PersonActivity.this, new FileUploadListener() { @Override public void success(final String url) { Person person = Person.getCurrentUser( Person.class); person.setUser_icon(url); ImageLoader.getInstance().displayImage(url, ivUserIcon); updateUser(person); } @Override public void fail() { dimissDialog(); HankkinUtils.showToast(PersonActivity.this, "上传失败"); } }); } } else if (requestCode == REQUST_CODE_CAMERA) { showLoadingDialog(); if (!TextUtils.isEmpty(filePath)) { Bitmap tempBitmap = BitmapUtils.getCompressedBitmap(PersonActivity.this, filePath); if (BitmapUtils.readPictureDegree(filePath) == 90) { tempBitmap = BitmapUtils.toturn(tempBitmap); } filePath = BitmapUtils.saveBitmap(tempBitmap,new Date().getTime() + ""); uploadImg(filePath, PersonActivity.this, new FileUploadListener() { @Override public void success(String url) { Person person = Person.getCurrentUser(Person.class); person.setUser_icon(url); // MyImageLoader.getInstance().displayImage(PersonActivity.this,url, ivUserIcon); ImageLoader.getInstance().displayImage(url, ivUserIcon); updateUser(person); } @Override public void fail() { dimissDialog(); HankkinUtils.showToast(PersonActivity.this, "上传失败"); } }); } } } /** * 更新个人用户信息 * by Hankkin at:2015-12-23 19:34:17 * * @param person */ private void updateUser(Person person) { person.update(person.getObjectId(), new UpdateListener() { @Override public void done(BmobException e) { if (e == null){ dimissDialog(); HankkinUtils.showToast(PersonActivity.this, "上传成功"); } } }); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/activity/ProdectDetailActivity.java ================================================ package com.hankkin.compustrading.activity; import android.os.Bundle; import android.support.design.widget.CollapsingToolbarLayout; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Gravity; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; import com.hankkin.compustrading.R; import com.hankkin.compustrading.Utils.HankkinUtils; import com.hankkin.compustrading.model.Product; import com.nostra13.universalimageloader.core.ImageLoader; public class ProdectDetailActivity extends AppCompatActivity { private Product product; private TextView tvDesc,tvProName,tvTime,tvSchool,tvPrice; private ImageView ivPro; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_prodect_detail); product = (Product) getIntent().getSerializableExtra("product"); tvDesc = (TextView) findViewById(R.id.tv_pro_desc); tvProName = (TextView) findViewById(R.id.tv_pro_name); ivPro = (ImageView) findViewById(R.id.iv_product); tvTime = (TextView) findViewById(R.id.tv_time); tvSchool = (TextView) findViewById(R.id.tv_school); tvPrice = (TextView) findViewById(R.id.tv_price); //透明状态栏 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); //透明导航栏 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.setNavigationIcon(android.support.v7.appcompat.R.drawable.abc_ic_ab_back_material); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); toolbar.inflateMenu(R.menu.menu_prodect_detail); toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.action_share: HankkinUtils.showToast(ProdectDetailActivity.this, "分享"); break; case R.id.action_settings: HankkinUtils.showToast(ProdectDetailActivity.this, "举报"); break; } return false; } }); CollapsingToolbarLayout collapsingAvatarToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar); collapsingAvatarToolbar.setBackgroundColor(getResources().getColor(R.color.theme_color)); collapsingAvatarToolbar.setExpandedTitleGravity(Gravity.CENTER_VERTICAL); collapsingAvatarToolbar.setExpandedTitleColor(getResources().getColor(R.color.theme_color)); TextView tv = (TextView) findViewById(R.id.username); tv.setText(product.getUsername()); tvProName.setText(product.getName()); tvDesc.setText(product.getDesc()); ImageLoader.getInstance().displayImage(product.getProduct_url(),ivPro); tvPrice.setText("¥" + product.getPrice()); tvSchool.setText(product.getSchool()); tvTime.setText(product.getCreatedAt()); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/activity/RegisterActivity.java ================================================ package com.hankkin.compustrading.activity; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import com.hankkin.compustrading.R; import com.hankkin.compustrading.Utils.HankkinUtils; import com.hankkin.compustrading.model.Person; import com.hankkin.compustrading.sharepreference.MySP; import com.pnikosis.materialishprogress.ProgressWheel; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; import cn.bmob.v3.BmobUser; import cn.bmob.v3.exception.BmobException; import cn.bmob.v3.listener.LogInListener; import cn.bmob.v3.listener.SaveListener; public class RegisterActivity extends AppCompatActivity { @Bind(R.id.btn_register) Button btnRegister; @Bind(R.id.et_login_name) EditText etName; @Bind(R.id.et_login_pwd) EditText etPwd; @Bind(R.id.tv_back) TextView tvBack; @Bind(R.id.pw_loading) ProgressWheel wheel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_register); ButterKnife.bind(this); initViews(); } private void initViews(){ wheel.stopSpinning(); } /** * 注册用户 * by Hankkin at:2015-12-20 21:13:02 */ @OnClick(R.id.btn_register) public void register(View view){ wheel.spin(); final String name = etName.getText().toString().trim(); final String pwd = etPwd.getText().toString().trim(); if (TextUtils.isEmpty(name)){ HankkinUtils.showToast(RegisterActivity.this,"用户名不能为空"); return; } if (TextUtils.isEmpty(pwd)){ HankkinUtils.showToast(RegisterActivity.this,"密码不能为空"); return; } if (!HankkinUtils.isMobileNO(name)){ HankkinUtils.showToast(RegisterActivity.this,"请输入正确的手机号"); return; } Person person = new Person(); person.setUsername(name); person.setPassword(pwd); person.signUp(new SaveListener() { @Override public void done(Object o, BmobException e) { if (e == null){ MySP.setPASSWoRD(RegisterActivity.this,pwd); MySP.setUSERNAME(RegisterActivity.this, name); wheel.stopSpinning(); HankkinUtils.showToast(RegisterActivity.this, "注册成功"); Intent intent = new Intent(RegisterActivity.this,MainShowActivity.class); startActivity(intent); finish(); if (LoginActivity.instance!=null){ LoginActivity.instance.finish(); } if (PersonActivity.instance!=null){ PersonActivity.instance.finish(); } if (MainShowActivity.instance!=null){ MainShowActivity.instance.finish(); } BmobUser.loginByAccount("username", "用户密码", new LogInListener() { @Override public void done(Person person, BmobException e) { if (e == null){ } } }); } else { wheel.stopSpinning(); HankkinUtils.showToast(RegisterActivity.this, "注册失败"); } } }); } @OnClick(R.id.tv_back) void back(){ finish(); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/activity/SearchProActivity.java ================================================ package com.hankkin.compustrading.activity; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import com.hankkin.compustrading.R; public class SearchProActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search_pro); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_search, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/activity/SplasActivity.java ================================================ package com.hankkin.compustrading.activity; import android.content.Intent; import android.os.Bundle; import android.os.Environment; import android.view.Menu; import android.view.MenuItem; import android.view.WindowManager; import com.hankkin.compustrading.R; import com.hankkin.compustrading.Utils.HankkinUtils; import com.hankkin.compustrading.model.Category; import java.io.File; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import cn.bmob.v3.BmobQuery; import cn.bmob.v3.exception.BmobException; import cn.bmob.v3.listener.FindListener; public class SplasActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splas); //透明状态栏 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); //透明导航栏 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); long size = new File(Environment.getExternalStorageDirectory().getPath() + File.separator + "compustrading" +"/app-release.apk").length(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); queryCategory(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_splas, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } /** * Bmob查询分类数据显示选项卡 * by Hankkin at:2015-11-29 19:39:45 */ private void queryCategory() { BmobQuery categoryBmobQuery = new BmobQuery<>(); categoryBmobQuery.order("createdAt");// 按照时间降序 categoryBmobQuery.findObjects(new FindListener() { @Override public void done(List list, BmobException e) { if (e == null){ if (list != null && list.size() > 0) { ArrayList categories = new ArrayList(); categories.addAll(list); Intent intent = new Intent(SplasActivity.this,MainShowActivity.class); Bundle bundle = new Bundle(); bundle.putSerializable("categories", (Serializable) categories); intent.putExtras(bundle); startActivity(intent); finish(); } } else { HankkinUtils.showToast(SplasActivity.this, e.getMessage()); } } }); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/adapter/CategoryFragmentAdapter.java ================================================ package com.hankkin.compustrading.adapter; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import com.hankkin.compustrading.fragment.CateDetailFragment; import com.hankkin.compustrading.model.Category; import java.util.ArrayList; import java.util.List; /** * Created by Hankkin on 15/11/29. */ public class CategoryFragmentAdapter extends FragmentPagerAdapter { private List fragments; private ArrayList categories; public CategoryFragmentAdapter(FragmentManager fm, List fragments,ArrayList categories) { super(fm); this.fragments = fragments; this.categories = categories; } public CategoryFragmentAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return fragments.get(position); } @Override public int getCount() { return categories.size(); } public String getPageTitle(int i){ return categories.get(i).getName(); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/adapter/PersonInfoAdapter.java ================================================ package com.hankkin.compustrading.adapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; import com.hankkin.compustrading.R; import com.hankkin.compustrading.model.PersonShow; import java.util.ArrayList; import java.util.HashMap; import java.util.List; /** * Created by Hankkin on 15/12/6. */ public class PersonInfoAdapter extends BaseAdapter { private List data = new ArrayList<>(); private Context context; private LayoutInflater inflater; public PersonInfoAdapter(List data, Context context) { this.data = data; this.context = context; this.inflater = LayoutInflater.from(context); } @Override public int getCount() { return data.size(); } @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null){ holder = new ViewHolder(); convertView = inflater.inflate(R.layout.listview_personinfo,null); holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title); holder.tvContent = (TextView) convertView.findViewById(R.id.tv_content); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.tvTitle.setText(data.get(position).getTitle()); holder.tvContent.setText(data.get(position).getContent()); return convertView; } class ViewHolder{ TextView tvTitle,tvContent; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/adapter/ProductAdapter.java ================================================ package com.hankkin.compustrading.adapter; import android.content.Context; import android.text.TextUtils; 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.hankkin.compustrading.R; import com.hankkin.compustrading.model.Product; import com.hankkin.compustrading.view.RoundedImageView; import com.nostra13.universalimageloader.core.ImageLoader; import java.util.List; /** * Created by Hankkin on 15/11/29. */ public class ProductAdapter extends BaseAdapter { private List data; private LayoutInflater inflater; private Context context; public ProductAdapter(List data, Context context) { this.data = data; this.context = context; inflater = LayoutInflater.from(context); } @Override public int getCount() { return data.size(); } @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null){ holder = new ViewHolder(); convertView = inflater.inflate(R.layout.lv_product_item,null); holder.ivProduct = (ImageView) convertView.findViewById(R.id.iv_product); holder.rivUserIcon = (RoundedImageView) convertView.findViewById(R.id.riv_usericon); holder.tvName = (TextView) convertView.findViewById(R.id.tv_name); holder.tvPrice = (TextView) convertView.findViewById(R.id.tv_price); holder.tvPubTime = (TextView) convertView.findViewById(R.id.tv_pub_time); holder.tvSchool = (TextView) convertView.findViewById(R.id.tv_school); holder.tvContent = (TextView) convertView.findViewById(R.id.tv_content); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } Product product = data.get(position); holder.tvSchool.setText(product.getSchool()); holder.tvPubTime.setText(product.getCreatedAt()); if (!TextUtils.isEmpty(product.getUsername())){ holder.tvName.setText(product.getUsername()); } else { holder.tvName.setText(product.getUser_tel()); } if (!TextUtils.isEmpty(product.getUser_icon_url())){ ImageLoader.getInstance().displayImage(product.getUser_icon_url(),holder.rivUserIcon); } else { holder.rivUserIcon.setImageDrawable(context.getResources().getDrawable(R.drawable.defaut)); } holder.tvPrice.setText("¥"+product.getPrice()); holder.tvContent.setText(product.getName()); ImageLoader.getInstance().displayImage(product.getProduct_url(),holder.ivProduct); return convertView; } class ViewHolder{ TextView tvPrice,tvPubTime,tvName,tvSchool,tvContent; ImageView ivProduct; RoundedImageView rivUserIcon; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/fragment/CateDetailFragment.java ================================================ package com.hankkin.compustrading.fragment; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.widget.SwipeRefreshLayout; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ListView; import com.hankkin.compustrading.R; import com.hankkin.compustrading.ScrollDirectionListener; import com.hankkin.compustrading.Utils.HankkinUtils; import com.hankkin.compustrading.activity.ProdectDetailActivity; import com.hankkin.compustrading.adapter.ProductAdapter; import com.hankkin.compustrading.model.Product; import com.hankkin.compustrading.view.RefreshLayout; import com.hankkin.compustrading.view.floatbutton.FloatingActionsMenu; import java.io.Serializable; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import cn.bmob.v3.BmobQuery; import cn.bmob.v3.datatype.BmobDate; import cn.bmob.v3.exception.BmobException; import cn.bmob.v3.listener.FindListener; /** * Created by Hankkin on 15/11/29. */ public class CateDetailFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener,RefreshLayout.OnLoadListener{ private RefreshLayout swipeRefreshLayout; private ListView lvProduct; private List productList = new ArrayList<>(); private int cid = 0; private ProductAdapter adapter; private FloatingActionsMenu fab; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { cid = getArguments().getInt("cid"); List data = (List) getArguments().getSerializable("products"); productList.clear(); for (int i = 0; i < data.size(); i++) { if (data.get(i).getCid() == cid) { productList.add(data.get(i)); } } } } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_item, container, false); initViews(view); return view; } private void initViews(View view) { lvProduct = (ListView) view.findViewById(R.id.lv_product); swipeRefreshLayout = (RefreshLayout) view.findViewById(R.id.swipeRefreshLayout); swipeRefreshLayout.setChildView(lvProduct); //设置卷内的颜色 swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright, android.R.color.holo_green_light, android.R.color.holo_orange_light, android.R.color.holo_red_light); swipeRefreshLayout.setOnLoadListener(this); swipeRefreshLayout.setOnRefreshListener(this); adapter = new ProductAdapter(productList, getActivity()); lvProduct.setAdapter(adapter); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); lvProduct.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { Intent intent = new Intent(getActivity(), ProdectDetailActivity.class); Bundle bundle = new Bundle(); bundle.putSerializable("product", (Serializable) adapter.getItem(position)); intent.putExtras(bundle); startActivity(intent); } }); if (fab!=null){ fab.attachToListView(lvProduct, new ScrollDirectionListener() { @Override public void onScrollDown() { Log.d("ListViewFragment", "onScrollDown()"); } @Override public void onScrollUp() { Log.d("ListViewFragment", "onScrollUp()"); } }, new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { Log.d("ListViewFragment", "onScrollStateChanged()"); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { Log.d("ListViewFragment", "onScroll()"); } }); } } /** * 根据商品Id查询商品 * by Hankkin at:2015-12-19 23:19:41 * @param cid */ private void queryProductsByIdHttp(int cid) { BmobQuery productBmobQuery = new BmobQuery<>(); productBmobQuery.order("-createdAt"); productBmobQuery.setLimit(10); productBmobQuery.addWhereEqualTo("cid", cid); productBmobQuery.findObjects(new FindListener() { @Override public void done(List list, BmobException e) { if (e == null){ if (list != null && list.size() > 0) { productList.clear(); productList.addAll(list); adapter = new ProductAdapter(productList, getActivity()); lvProduct.setAdapter(adapter); swipeRefreshLayout.setRefreshing(false); } } else { HankkinUtils.showLToast(getActivity(),e.getMessage()); } } }); } private void queryProductsByIdHttp1(int cid) { BmobQuery productBmobQuery = new BmobQuery<>(); Product product = (Product) adapter.getItem(adapter.getCount() - 1); String time = product.getCreatedAt(); SimpleDateFormat sdf = new SimpleDateFormat(time); try { Date date = sdf.parse(time); productBmobQuery.addWhereLessThan("createdAt",new BmobDate(date)); } catch (ParseException e) { e.printStackTrace(); } productBmobQuery.order("-createdAt"); productBmobQuery.setLimit(10); productBmobQuery.addWhereEqualTo("cid", cid); productBmobQuery.findObjects(new FindListener() { @Override public void done(List list, BmobException e) { if (e == null){ if (list != null && list.size() > 0) { productList.addAll(list); adapter.notifyDataSetChanged(); swipeRefreshLayout.setLoading(false); } else { HankkinUtils.showToast(getActivity().getApplicationContext(), "暂无新数据"); swipeRefreshLayout.setLoading(false); } }else { HankkinUtils.showLToast(getActivity(),e.getMessage()); } } }); } public void updatePeo() { swipeRefreshLayout.setRefreshing(true); new Handler().postDelayed(new Runnable() { @Override public void run() { queryProductsByIdHttp(cid); } }, 2000); } @Override public void onLoad() { swipeRefreshLayout.postDelayed(new Runnable() { @Override public void run() { queryProductsByIdHttp1(cid); } }, 2000); } @Override public void onRefresh() { swipeRefreshLayout.postDelayed(new Runnable() { @Override public void run() { queryProductsByIdHttp(cid); } }, 2000); } public ListView getLvProduct(){ return lvProduct; } public void setFab(FloatingActionsMenu fab){ this.fab = fab; } public void hideFab(){ fab.toggle(); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/interface/FileUploadListener.java ================================================ package com.hankkin.compustrading; /** * Created by Hankkin on 15/12/20. */ public interface FileUploadListener { void success(String url); void fail(); } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/interface/ScrollDirectionListener.java ================================================ package com.hankkin.compustrading; public interface ScrollDirectionListener { void onScrollDown(); void onScrollUp(); } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/model/Category.java ================================================ package com.hankkin.compustrading.model; import cn.bmob.v3.BmobObject; /** * Created by Hankkin on 15/11/29. */ public class Category extends BmobObject{ private Integer id; private String name; private String desc; private int pid; public Category(Integer id,String name, String desc, int pid) { this.id = id; this.name = name; this.desc = desc; this.pid = pid; } 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 String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public int getPid() { return pid; } public void setPid(int pid) { this.pid = pid; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/model/Person.java ================================================ package com.hankkin.compustrading.model; import cn.bmob.v3.BmobObject; import cn.bmob.v3.BmobUser; /** * Created by Hankkin on 15/12/6. */ public class Person extends BmobUser { private String name; private String tel; private String user_icon; private String nickname; private String sex; private String birth; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTel() { return tel; } public void setTel(String tel) { this.tel = tel; } public String getUser_icon() { return user_icon; } public void setUser_icon(String user_icon) { this.user_icon = user_icon; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getBirth() { return birth; } public void setBirth(String birth) { this.birth = birth; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/model/PersonShow.java ================================================ package com.hankkin.compustrading.model; /** * Created by Hankkin on 15/12/6. */ public class PersonShow { private String title; private String content; public PersonShow(String title, String content) { this.title = title; this.content = content; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/model/Product.java ================================================ package com.hankkin.compustrading.model; import cn.bmob.v3.BmobObject; /** * Created by Hankkin on 15/11/29. */ public class Product extends BmobObject { private int id; private String name; private String price; private String desc; private String user_tel; private int cid; private String product_url; private String school; private String username; private String user_icon_url; public Product() { } public Product(int id,String name,String price,String desc,String user_tel,int category_id){ this.id=id; this.name = name; this.price=price; this.desc=desc; this.user_tel=user_tel; this.cid=category_id; } 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 String getPrice() { return price; } public void setPrice(String price) { this.price = price; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getUser_tel() { return user_tel; } public void setUser_tel(String user_tel) { this.user_tel = user_tel; } public int getCid() { return cid; } public void setCid(int cid) { this.cid = cid; } public String getProduct_url() { return product_url; } public void setProduct_url(String product_url) { this.product_url = product_url; } public String getSchool() { return school; } public void setSchool(String school) { this.school = school; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUser_icon_url() { return user_icon_url; } public void setUser_icon_url(String user_icon_url) { this.user_icon_url = user_icon_url; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/sharepreference/MySP.java ================================================ package com.hankkin.compustrading.sharepreference; import android.content.Context; import android.content.SharedPreferences; /** * Created by Hankkin on 15/12/20. */ public class MySP { private static final String PREFERENCE_NAME = "SYSTEM"; static SharedPreferences mSP = null; public static String USERNAME = "username"; public static String PASSWORD = "password"; public static String getUSERNAME(Context context) { return getStringData(context,PASSWORD); } public static void setUSERNAME(Context context,String username) { saveData(context,USERNAME,username); } public static String getPASSWoRD(Context context) { return getStringData(context,PASSWORD); } public static void setPASSWoRD(Context context,String password) { saveData(context,PASSWORD,password); } static boolean saveData(Context context, String key, Object value) { if (context == null || key == null || value == null) { return false; } if (mSP == null) { mSP = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); } SharedPreferences.Editor editor = mSP.edit(); if (value instanceof Boolean) { editor.putBoolean(key, (Boolean) value); } else if (value instanceof Float) { editor.putFloat(key, (Float) value); } else if (value instanceof Integer) { editor.putInt(key, (Integer) value); } else if (value instanceof Long) { editor.putLong(key, (Long) value); } else if (value instanceof String) { editor.putString(key, (String) value); } else { return false; } return editor.commit(); } static String getStringData(Context context, String key) { String value = null; if (context == null || key == null) { return value; } if (mSP == null) { mSP = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); } value = mSP.getString(key, null); return value; } static boolean getBooleanData(Context context, String key) { boolean value = false; if (context == null || key == null) { return value; } if (mSP == null) { mSP = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); } value = mSP.getBoolean(key, false); return value; } static int getIntData(Context context, String key) { int value = 0; if (context == null || key == null) { return value; } if (mSP == null) { mSP = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); } value = mSP.getInt(key, 0); return value; } static long getLongData(Context context, String key) { long value = -1; if (context == null || key == null) { return value; } if (mSP == null) { mSP = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); } value = mSP.getLong(key, 0); return value; } static boolean deleteKey(Context context, String key) { if (context == null || key == null) { return false; } if (mSP == null) { mSP = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); } SharedPreferences.Editor editor = mSP.edit(); editor.remove(key); return editor.commit(); } static boolean deleteKeys(Context context, String... keys) { if (context == null || keys == null) { return false; } if (mSP == null) { mSP = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); } SharedPreferences.Editor editor = mSP.edit(); for (int i = 0; i < keys.length; i++) { editor.remove(keys[i]); } return editor.commit(); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/slidingmenu/CustomViewAbove.java ================================================ package com.hankkin.compustrading.slidingmenu; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Build; import android.support.v4.view.KeyEventCompat; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.VelocityTrackerCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewConfigurationCompat; import android.util.AttributeSet; import android.util.FloatMath; import android.util.Log; import android.view.FocusFinder; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.widget.Scroller; public class CustomViewAbove extends ViewGroup { private static final String TAG = "CustomViewAbove"; private static final boolean DEBUG = false; //是否使用缓存 private static final boolean USE_CACHE = false; //最大持续的时间 private static final int MAX_SETTLE_DURATION = 600; // ms //最小滑动的距离 private static final int MIN_DISTANCE_FOR_FLING = 25; // dips /** * 定义一个修饰动画的效果类 * Interpolator被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果可以 accelerated(加速),decelerated(减速),repeated(重复),bounced(弹跳)等。 */ private static final Interpolator sInterpolator = new Interpolator() { public float getInterpolation(float t) { t -= 1.0f; return t * t * t * t * t + 1.0f; } }; //定义内容视图 private View mContent; //当前的选项 private int mCurItem; //滚动滑轮 private Scroller mScroller; //是否能够使用滑动缓存 private boolean mScrollingCacheEnabled; //是否正在滑动 private boolean mScrolling; //是否正在拖动 private boolean mIsBeingDragged; //是否能够拖动 private boolean mIsUnableToDrag; //定义触摸溢出的值 private int mTouchSlop; //初始化触摸屏幕X轴的值 private float mInitialMotionX; //最后移动到的X、Y的坐标 private float mLastMotionX,mLastMotionY; /** * 定义一个活动指针,在多点触摸的时候调用 */ protected int mActivePointerId = INVALID_POINTER; /** * 为当前的活动指针赋值 */ private static final int INVALID_POINTER = -1; /** * 触摸滚动期间的绝对速度 */ protected VelocityTracker mVelocityTracker; //最小滑动速度值 private int mMinimumVelocity; //最大滑动速度值 protected int mMaximumVelocity; //滑动的距离 private int mFlingDistance; //定义下方视图对象 private CustomViewBehind mViewBehind; //是否能够使用 private boolean mEnabled = true; //页面改变监听器 private OnPageChangeListener mOnPageChangeListener; //内部页面改变监听器 private OnPageChangeListener mInternalPageChangeListener; //关闭监听器 private SlidingMenu.OnClosedListener mClosedListener; //打开监听器 private SlidingMenu.OnOpenedListener mOpenedListener; //存放被忽略的视图组件列表 private List mIgnoredViews = new ArrayList(); /** * 调用此接口去响应改变选中页面的状态 */ public interface OnPageChangeListener { /** * This method will be invoked when the current page is scrolled, either as part * of a programmatically initiated smooth scroll or a user initiated touch scroll. * * @param position Position index of the first page currently being displayed. * Page position+1 will be visible if positionOffset is nonzero. * @param positionOffset Value from [0, 1) indicating the offset from the page at position. * @param positionOffsetPixels Value in pixels indicating the offset from position. */ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); /** * This method will be invoked when a new page becomes selected. Animation is not * necessarily complete. * * @param position Position index of the new selected page. */ public void onPageSelected(int position); } /** * Simple implementation of the {@link OnPageChangeListener} interface with stub * implementations of each method. Extend this if you do not intend to override * every method of {@link OnPageChangeListener}. */ public static class SimpleOnPageChangeListener implements OnPageChangeListener { public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // This space for rent } public void onPageSelected(int position) { // This space for rent } public void onPageScrollStateChanged(int state) { // This space for rent } } public CustomViewAbove(Context context) { this(context, null); } public CustomViewAbove(Context context, AttributeSet attrs) { super(context, attrs); initCustomViewAbove(); } /** * 初始化最上方视图 */ void initCustomViewAbove() { //设置是否能够调用自定义的布局,false是可以 setWillNotDraw(false); //优先其子类控件而获取到焦点 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); //设置是否能够获取焦点 setFocusable(true); //得到上下文 final Context context = getContext(); //实例化滚动器 mScroller = new Scroller(context, sInterpolator); final ViewConfiguration configuration = ViewConfiguration.get(context); //获得能够进行手势滑动的距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件 mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); //获得允许执行一个fling手势动作的最小速度值 mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); //获得允许执行一个fling手势动作的最大速度值 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); setInternalPageChangeListener(new SimpleOnPageChangeListener() { public void onPageSelected(int position) { if (mViewBehind != null) { switch (position) { case 0: case 2: mViewBehind.setChildrenEnabled(true); break; case 1: mViewBehind.setChildrenEnabled(false); break; } } } }); //获得该手机设备的屏幕密度值 final float density = context.getResources().getDisplayMetrics().density; //滑动的距离 mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density); } /** * 设置当前选中的项 */ public void setCurrentItem(int item) { setCurrentItemInternal(item, true, false); } /** * 设置当前选中的项,是否平滑的过渡到选中项的页面 */ public void setCurrentItem(int item, boolean smoothScroll) { setCurrentItemInternal(item, smoothScroll, false); } /** * 得到当前选中的项 */ public int getCurrentItem() { return mCurItem; } /** * 设置当前内部选中的项 */ void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) { setCurrentItemInternal(item, smoothScroll, always, 0); } /** * 设置当前内部选中的项 */ void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) { if (!always && mCurItem == item) { setScrollingCacheEnabled(false); return; } item = mViewBehind.getMenuPage(item); final boolean dispatchSelected = mCurItem != item; mCurItem = item; final int destX = getDestScrollX(mCurItem); if (dispatchSelected && mOnPageChangeListener != null) { mOnPageChangeListener.onPageSelected(item); } if (dispatchSelected && mInternalPageChangeListener != null) { mInternalPageChangeListener.onPageSelected(item); } if (smoothScroll) { smoothScrollTo(destX, 0, velocity); } else { completeScroll(); scrollTo(destX, 0); } } /** * 设置一个监听事件当页面改变或者加速滚动的时候调用 */ public void setOnPageChangeListener(OnPageChangeListener listener) { mOnPageChangeListener = listener; } /** * 设置打开监听事件 */ public void setOnOpenedListener(SlidingMenu.OnOpenedListener l) { mOpenedListener = l; } /** * 设置关闭监听事件 */ public void setOnClosedListener(SlidingMenu.OnClosedListener l) { mClosedListener = l; } /** * Set a separate OnPageChangeListener for internal use by the support library. * * @param listener Listener to set * @return The old listener that was set, if any. */ OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) { OnPageChangeListener oldListener = mInternalPageChangeListener; mInternalPageChangeListener = listener; return oldListener; } /** * 添加被忽略的组件 */ public void addIgnoredView(View v) { if (!mIgnoredViews.contains(v)) { mIgnoredViews.add(v); } } /** * 移除被忽略的组件 */ public void removeIgnoredView(View v) { mIgnoredViews.remove(v); } /** * 清空被忽略的组件 */ public void clearIgnoredViews() { mIgnoredViews.clear(); } // We want the duration of the page snap animation to be influenced by the distance that // the screen has to travel, however, we don't want this duration to be effected in a // purely linear fashion. Instead, we use this method to moderate the effect that the distance // of travel has on the overall snap duration. float distanceInfluenceForSnapDuration(float f) { f -= 0.5f; // center the values about 0. f *= 0.3f * Math.PI / 2.0f; return (float) Math.sin(f); } /** * 得到滑动到的X轴的坐标 */ public int getDestScrollX(int page) { switch (page) { case 0: case 2: return mViewBehind.getMenuLeft(mContent, page); case 1: return mContent.getLeft(); } return 0; } /** * 得到左边框 */ private int getLeftBound() { return mViewBehind.getAbsLeftBound(mContent); } /** * 得到右边框 */ private int getRightBound() { return mViewBehind.getAbsRightBound(mContent); } public int getContentLeft() { return mContent.getLeft() + mContent.getPaddingLeft(); } /** * 得到滑动菜单是否打开 */ public boolean isMenuOpen() { return mCurItem == 0 || mCurItem == 2; } /** * 是否忽略视图 */ private boolean isInIgnoredView(MotionEvent ev) { Rect rect = new Rect(); for (View v : mIgnoredViews) { v.getHitRect(rect); if (rect.contains((int)ev.getX(), (int)ev.getY())) return true; } return false; } /** * 得到下方视图的宽度 */ public int getBehindWidth() { if (mViewBehind == null) { return 0; } else { return mViewBehind.getBehindWidth(); } } /** * 得到子控件的宽度 */ public int getChildWidth(int i) { switch (i) { case 0: return getBehindWidth(); case 1: return mContent.getWidth(); default: return 0; } } /** * 得到是否能够滑动 */ public boolean isSlidingEnabled() { return mEnabled; } /** * 设置是否能够滑动 */ public void setSlidingEnabled(boolean b) { mEnabled = b; } /** * 平滑的滑动到指定的位置 */ void smoothScrollTo(int x, int y) { smoothScrollTo(x, y, 0); } /** * 通过设置速度来平滑的滑动到指定的位置 */ void smoothScrollTo(int x, int y, int velocity) { if (getChildCount() == 0) { // Nothing to do. setScrollingCacheEnabled(false); return; } //获得当前View显示部分的左边到第一个View的左边的距离 int sx = getScrollX(); int sy = getScrollY(); int dx = x - sx; int dy = y - sy; //如果都等于0,说明正好是滑动了一个屏幕的距离 if (dx == 0 && dy == 0) { completeScroll(); if (isMenuOpen()) { if (mOpenedListener != null) mOpenedListener.onOpened(); } else { if (mClosedListener != null) mClosedListener.onClosed(); } return; } setScrollingCacheEnabled(true); mScrolling = true; //获得下方视图的宽度 final int width = getBehindWidth(); final int halfWidth = width / 2; //取两数中最小的值赋给滑动距离与下方视图宽度的比值 final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width); //获得当前滑动的距离 final float distance = halfWidth + halfWidth * distanceInfluenceForSnapDuration(distanceRatio); //初始化持续的时间 int duration = 0; //获得速度的绝对值 velocity = Math.abs(velocity); if (velocity > 0) { //Math.round()四舍五入 duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); } else { final float pageDelta = (float) Math.abs(dx) / width; duration = (int) ((pageDelta + 1) * 100); duration = MAX_SETTLE_DURATION; } //取两数中最小的一个值赋给持续的时间 duration = Math.min(duration, MAX_SETTLE_DURATION); //开始滑动 mScroller.startScroll(sx, sy, dx, dy, duration); //刷新界面 invalidate(); } /** * 设置内容视图 */ public void setContent(View v) { if (mContent != null) this.removeView(mContent); mContent = v; addView(mContent); } /** * 得到内容视图 */ public View getContent() { return mContent; } /** * 设置下方视图 */ public void setCustomViewBehind(CustomViewBehind cvb) { mViewBehind = cvb; } /** * 在父元素正要放置该控件时调用。它会问一个问题,“你想要用多大地方啊?”,然后传入两个参数——widthMeasureSpec和heightMeasureSpec。 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = getDefaultSize(0, widthMeasureSpec); int height = getDefaultSize(0, heightMeasureSpec); setMeasuredDimension(width, height); final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width); final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height); mContent.measure(contentWidth, contentHeight); } /** * 当视图尺寸改变的时候调用 */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // Make sure scroll position is set correctly. if (w != oldw) { // [ChrisJ] - This fixes the onConfiguration change for orientation issue.. // maybe worth having a look why the recomputeScroll pos is screwing // up? completeScroll(); scrollTo(getDestScrollX(mCurItem), getScrollY()); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int width = r - l; final int height = b - t; mContent.layout(0, 0, width, height); } /** * 设置上方视图的偏移量 */ public void setAboveOffset(int i) { mContent.setPadding(i, mContent.getPaddingTop(), mContent.getPaddingRight(), mContent.getPaddingBottom()); } @Override public void computeScroll() { if (!mScroller.isFinished()) { if (mScroller.computeScrollOffset()) { int oldX = getScrollX(); int oldY = getScrollY(); int x = mScroller.getCurrX(); int y = mScroller.getCurrY(); if (oldX != x || oldY != y) { scrollTo(x, y); pageScrolled(x); } // Keep on drawing until the animation has finished. invalidate(); return; } } //完成滑动,清除状态 completeScroll(); } /** * 页面滚动 */ private void pageScrolled(int xpos) { final int widthWithMargin = getWidth(); final int position = xpos / widthWithMargin; final int offsetPixels = xpos % widthWithMargin; final float offset = (float) offsetPixels / widthWithMargin; onPageScrolled(position, offset, offsetPixels); } /** * 页面滚动 * * @param position Position index of the first page currently being displayed. * Page position+1 will be visible if positionOffset is nonzero. * @param offset Value from [0, 1) indicating the offset from the page at position. * @param offsetPixels Value in pixels indicating the offset from position. */ protected void onPageScrolled(int position, float offset, int offsetPixels) { if (mOnPageChangeListener != null) { mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels); } if (mInternalPageChangeListener != null) { mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels); } } /** * 完成滑动 */ private void completeScroll() { //是否需要移动 boolean needPopulate = mScrolling; if (needPopulate) { // Done with scroll, no longer want to cache view drawing. setScrollingCacheEnabled(false); //终止动画效果 mScroller.abortAnimation(); //获得滚动条初始的坐标 int oldX = getScrollX(); int oldY = getScrollY(); //获得滚动条当前的坐标 int x = mScroller.getCurrX(); int y = mScroller.getCurrY(); //如果滚动条初始的坐标和当前的坐标不等则滑动 if (oldX != x || oldY != y) { scrollTo(x, y); } if (isMenuOpen()) { if (mOpenedListener != null) mOpenedListener.onOpened(); } else { if (mClosedListener != null) mClosedListener.onClosed(); } } //将滑动的状态设置为false mScrolling = false; } //获得触摸模式的值 protected int mTouchMode = SlidingMenu.TOUCHMODE_MARGIN; /** * 设置触摸的模式 */ public void setTouchMode(int i) { mTouchMode = i; } /** * 得到触摸的模式 */ public int getTouchMode() { return mTouchMode; } /** * 判断是否允许触摸打开滑动菜单 */ private boolean thisTouchAllowed(MotionEvent ev) { int x = (int) (ev.getX() + mScrollX); if (isMenuOpen()) { return mViewBehind.menuOpenTouchAllowed(mContent, mCurItem, x); } else { switch (mTouchMode) { case SlidingMenu.TOUCHMODE_FULLSCREEN: return !isInIgnoredView(ev); case SlidingMenu.TOUCHMODE_NONE: return false; case SlidingMenu.TOUCHMODE_MARGIN: return mViewBehind.marginTouchAllowed(mContent, x); } } return false; } /** * 判断是否允许滑动 */ private boolean thisSlideAllowed(float dx) { boolean allowed = false; if (isMenuOpen()) { allowed = mViewBehind.menuOpenSlideAllowed(dx); } else { allowed = mViewBehind.menuClosedSlideAllowed(dx); } if (DEBUG) Log.v(TAG, "this slide allowed " + allowed + " dx: " + dx); return allowed; } /** * 得到指针的索引值 */ private int getPointerIndex(MotionEvent ev, int id) { int activePointerIndex = MotionEventCompat.findPointerIndex(ev, id); if (activePointerIndex == -1) mActivePointerId = INVALID_POINTER; return activePointerIndex; } private boolean mQuickReturn = false; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (!mEnabled) return false; final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; if (DEBUG) if (action == MotionEvent.ACTION_DOWN) Log.v(TAG, "Received ACTION_DOWN"); if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP || (action != MotionEvent.ACTION_DOWN && mIsUnableToDrag)) { endDrag(); return false; } switch (action) { case MotionEvent.ACTION_MOVE: determineDrag(ev); break; case MotionEvent.ACTION_DOWN: int index = MotionEventCompat.getActionIndex(ev); mActivePointerId = MotionEventCompat.getPointerId(ev, index); if (mActivePointerId == INVALID_POINTER) break; mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index); mLastMotionY = MotionEventCompat.getY(ev, index); if (thisTouchAllowed(ev)) { mIsBeingDragged = false; mIsUnableToDrag = false; if (isMenuOpen() && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) { mQuickReturn = true; } } else { mIsUnableToDrag = true; } break; case MotionEventCompat.ACTION_POINTER_UP: onSecondaryPointerUp(ev); break; } if (!mIsBeingDragged) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); } return mIsBeingDragged || mQuickReturn; } @Override public boolean onTouchEvent(MotionEvent ev) { if (!mEnabled) return false; if (!mIsBeingDragged && !thisTouchAllowed(ev)) return false; // if (!mIsBeingDragged && !mQuickReturn) // return false; final int action = ev.getAction(); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); switch (action & MotionEventCompat.ACTION_MASK) { case MotionEvent.ACTION_DOWN: /* * If being flinged and user touches, stop the fling. isFinished * will be false if being flinged. */ completeScroll(); // Remember where the motion event started int index = MotionEventCompat.getActionIndex(ev); mActivePointerId = MotionEventCompat.getPointerId(ev, index); mLastMotionX = mInitialMotionX = ev.getX(); break; case MotionEvent.ACTION_MOVE: if (!mIsBeingDragged) { determineDrag(ev); if (mIsUnableToDrag) return false; } if (mIsBeingDragged) { // Scroll to follow the motion event final int activePointerIndex = getPointerIndex(ev, mActivePointerId); if (mActivePointerId == INVALID_POINTER) break; final float x = MotionEventCompat.getX(ev, activePointerIndex); final float deltaX = mLastMotionX - x; mLastMotionX = x; float oldScrollX = getScrollX(); float scrollX = oldScrollX + deltaX; final float leftBound = getLeftBound(); final float rightBound = getRightBound(); if (scrollX < leftBound) { scrollX = leftBound; } else if (scrollX > rightBound) { scrollX = rightBound; } // Don't lose the rounded component mLastMotionX += scrollX - (int) scrollX; scrollTo((int) scrollX, getScrollY()); pageScrolled((int) scrollX); } break; case MotionEvent.ACTION_UP: if (mIsBeingDragged) { final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int initialVelocity = (int) VelocityTrackerCompat.getXVelocity( velocityTracker, mActivePointerId); final int scrollX = getScrollX(); // final int widthWithMargin = getWidth(); // final float pageOffset = (float) (scrollX % widthWithMargin) / widthWithMargin; // TODO test this. should get better flinging behavior final float pageOffset = (float) (scrollX - getDestScrollX(mCurItem)) / getBehindWidth(); final int activePointerIndex = getPointerIndex(ev, mActivePointerId); if (mActivePointerId != INVALID_POINTER) { final float x = MotionEventCompat.getX(ev, activePointerIndex); final int totalDelta = (int) (x - mInitialMotionX); int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta); setCurrentItemInternal(nextPage, true, true, initialVelocity); } else { setCurrentItemInternal(mCurItem, true, true, initialVelocity); } mActivePointerId = INVALID_POINTER; endDrag(); } else if (mQuickReturn && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) { // close the menu setCurrentItem(1); endDrag(); } break; case MotionEvent.ACTION_CANCEL: if (mIsBeingDragged) { setCurrentItemInternal(mCurItem, true, true); mActivePointerId = INVALID_POINTER; endDrag(); } break; case MotionEventCompat.ACTION_POINTER_DOWN: { final int indexx = MotionEventCompat.getActionIndex(ev); mLastMotionX = MotionEventCompat.getX(ev, indexx); mActivePointerId = MotionEventCompat.getPointerId(ev, indexx); break; } case MotionEventCompat.ACTION_POINTER_UP: onSecondaryPointerUp(ev); int pointerIndex = getPointerIndex(ev, mActivePointerId); if (mActivePointerId == INVALID_POINTER) break; mLastMotionX = MotionEventCompat.getX(ev, pointerIndex); break; } return true; } private void determineDrag(MotionEvent ev) { final int activePointerId = mActivePointerId; final int pointerIndex = getPointerIndex(ev, activePointerId); if (activePointerId == INVALID_POINTER) return; final float x = MotionEventCompat.getX(ev, pointerIndex); final float dx = x - mLastMotionX; final float xDiff = Math.abs(dx); final float y = MotionEventCompat.getY(ev, pointerIndex); final float dy = y - mLastMotionY; final float yDiff = Math.abs(dy); if (xDiff > (isMenuOpen()?mTouchSlop/2:mTouchSlop) && xDiff > yDiff && thisSlideAllowed(dx)) { startDrag(); mLastMotionX = x; mLastMotionY = y; setScrollingCacheEnabled(true); // TODO add back in touch slop check } else if (xDiff > mTouchSlop) { mIsUnableToDrag = true; } } @Override public void scrollTo(int x, int y) { super.scrollTo(x, y); mScrollX = x; mViewBehind.scrollBehindTo(mContent, x, y); ((SlidingMenu)getParent()).manageLayers(getPercentOpen()); } private int determineTargetPage(float pageOffset, int velocity, int deltaX) { int targetPage = mCurItem; if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) { if (velocity > 0 && deltaX > 0) { targetPage -= 1; } else if (velocity < 0 && deltaX < 0){ targetPage += 1; } } else { targetPage = (int) Math.round(mCurItem + pageOffset); } return targetPage; } protected float getPercentOpen() { return Math.abs(mScrollX-mContent.getLeft()) / getBehindWidth(); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); // Draw the margin drawable if needed. mViewBehind.drawShadow(mContent, canvas); mViewBehind.drawFade(mContent, canvas, getPercentOpen()); mViewBehind.drawSelector(mContent, canvas, getPercentOpen()); } // variables for drawing private float mScrollX = 0.0f; private void onSecondaryPointerUp(MotionEvent ev) { if (DEBUG) Log.v(TAG, "onSecondaryPointerUp called"); final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, 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; mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex); mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); if (mVelocityTracker != null) { mVelocityTracker.clear(); } } } /** * 开始拖动 */ private void startDrag() { mIsBeingDragged = true; mQuickReturn = false; } /** * 结束拖动 */ private void endDrag() { mQuickReturn = false; mIsBeingDragged = false; mIsUnableToDrag = false; mActivePointerId = INVALID_POINTER; if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } /** * 设置能否使用滑动缓存 */ private void setScrollingCacheEnabled(boolean enabled) { if (mScrollingCacheEnabled != enabled) { mScrollingCacheEnabled = enabled; if (USE_CACHE) { final int size = getChildCount(); for (int i = 0; i < size; ++i) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { child.setDrawingCacheEnabled(enabled); } } } } } /** * Tests scrollability within child views of v given a delta of dx. * * @param v View to test for horizontal scrollability * @param checkV Whether the view v passed should itself be checked for scrollability (true), * or just its children (false). * @param dx Delta scrolled in pixels * @param x X coordinate of the active touch point * @param y Y coordinate of the active touch point * @return true if child views of v can be scrolled by delta of dx. */ protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { if (v instanceof ViewGroup) { final ViewGroup group = (ViewGroup) v; final int scrollX = v.getScrollX(); final int scrollY = v.getScrollY(); final int count = group.getChildCount(); // Count backwards - let topmost views consume scroll distance first. for (int i = count - 1; i >= 0; i--) { final View child = group.getChildAt(i); if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() && y + scrollY >= child.getTop() && y + scrollY < child.getBottom() && canScroll(child, true, dx, x + scrollX - child.getLeft(), y + scrollY - child.getTop())) { return true; } } } return checkV && ViewCompat.canScrollHorizontally(v, -dx); } @Override public boolean dispatchKeyEvent(KeyEvent event) { // Let the focused view and/or our descendants get the key first return super.dispatchKeyEvent(event) || executeKeyEvent(event); } /** * 执行按键响应事件 */ public boolean executeKeyEvent(KeyEvent event) { boolean handled = false; if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_LEFT: handled = arrowScroll(FOCUS_LEFT); break; case KeyEvent.KEYCODE_DPAD_RIGHT: handled = arrowScroll(FOCUS_RIGHT); break; case KeyEvent.KEYCODE_TAB: if (Build.VERSION.SDK_INT >= 11) { // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD // before Android 3.0. Ignore the tab key on those devices. if (KeyEventCompat.hasNoModifiers(event)) { handled = arrowScroll(FOCUS_FORWARD); } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) { handled = arrowScroll(FOCUS_BACKWARD); } } break; } } return handled; } /** * 获得滑动的方向 */ public boolean arrowScroll(int direction) { View currentFocused = findFocus(); if (currentFocused == this) currentFocused = null; boolean handled = false; View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); if (nextFocused != null && nextFocused != currentFocused) { if (direction == View.FOCUS_LEFT) { handled = nextFocused.requestFocus(); } else if (direction == View.FOCUS_RIGHT) { // If there is nothing to the right, or this is causing us to // jump to the left, then what we really want to do is page right. if (currentFocused != null && nextFocused.getLeft() <= currentFocused.getLeft()) { handled = pageRight(); } else { handled = nextFocused.requestFocus(); } } } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) { // Trying to move left and nothing there; try to page. handled = pageLeft(); } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) { // Trying to move right and nothing there; try to page. handled = pageRight(); } if (handled) { playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); } return handled; } /** * 页面是否向左移动 */ boolean pageLeft() { if (mCurItem > 0) { setCurrentItem(mCurItem-1, true); return true; } return false; } /** * 页面是否向右移动 */ boolean pageRight() { if (mCurItem < 1) { setCurrentItem(mCurItem+1, true); return true; } return false; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/slidingmenu/CustomViewBehind.java ================================================ package com.hankkin.compustrading.slidingmenu; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import com.hankkin.compustrading.R; public class CustomViewBehind extends ViewGroup { private static final String TAG = "CustomViewBehind"; //边缘滑动的临界值 private static final int MARGIN_THRESHOLD = 48; // dips //初始化触摸的模式 private int mTouchMode = SlidingMenu.TOUCHMODE_MARGIN; //定义上方视图 private CustomViewAbove mViewAbove; //定义内容视图 private View mContent; private View mSecondaryContent; //定义滑动边缘的临界值 private int mMarginThreshold; //宽度的偏移量 private int mWidthOffset; private SlidingMenu.CanvasTransformer mTransformer; //是否能够使用子视图 private boolean mChildrenEnabled; public CustomViewBehind(Context context) { this(context, null); } public CustomViewBehind(Context context, AttributeSet attrs) { super(context, attrs); mMarginThreshold = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, MARGIN_THRESHOLD, getResources().getDisplayMetrics()); } public void setCustomViewAbove(CustomViewAbove customViewAbove) { mViewAbove = customViewAbove; } public void setCanvasTransformer(SlidingMenu.CanvasTransformer t) { mTransformer = t; } /** * 设置宽度的偏移量 */ public void setWidthOffset(int i) { mWidthOffset = i; requestLayout(); } /** * 设置边缘滑动的临界值 */ public void setMarginThreshold(int marginThreshold) { mMarginThreshold = marginThreshold; } /** * 得到边缘滑动的临界值 */ public int getMarginThreshold() { return mMarginThreshold; } /** * 得到视图的宽度 */ public int getBehindWidth() { return mContent.getWidth(); } /** * 设置视图的内容 */ public void setContent(View v) { if (mContent != null) removeView(mContent); mContent = v; addView(mContent); } /** * 得到视图的内容 */ public View getContent() { return mContent; } /** * 设置右边滑动菜单的内容,当模式设置为LEFT_RIGHT模式时 */ public void setSecondaryContent(View v) { if (mSecondaryContent != null) removeView(mSecondaryContent); mSecondaryContent = v; addView(mSecondaryContent); } /** * 得到右边滑动菜单的内容 */ public View getSecondaryContent() { return mSecondaryContent; } /** * 设置是否能够使用子视图 */ public void setChildrenEnabled(boolean enabled) { mChildrenEnabled = enabled; } @Override public void scrollTo(int x, int y) { super.scrollTo(x, y); if (mTransformer != null) invalidate(); } @Override public boolean onInterceptTouchEvent(MotionEvent e) { return !mChildrenEnabled; } @Override public boolean onTouchEvent(MotionEvent e) { return !mChildrenEnabled; } @Override protected void dispatchDraw(Canvas canvas) { if (mTransformer != null) { canvas.save(); mTransformer.transformCanvas(canvas, mViewAbove.getPercentOpen()); super.dispatchDraw(canvas); canvas.restore(); } else super.dispatchDraw(canvas); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int width = r - l; final int height = b - t; mContent.layout(0, 0, width-mWidthOffset, height); if (mSecondaryContent != null) mSecondaryContent.layout(0, 0, width-mWidthOffset, height); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = getDefaultSize(0, widthMeasureSpec); int height = getDefaultSize(0, heightMeasureSpec); setMeasuredDimension(width, height); final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width-mWidthOffset); final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height); mContent.measure(contentWidth, contentHeight); if (mSecondaryContent != null) mSecondaryContent.measure(contentWidth, contentHeight); } //定义模式的值 private int mMode; //是否能够使用渐入渐出效果 private boolean mFadeEnabled; //定义渐入渐出的值 private float mFadeDegree; //定义渐入渐出效果画笔 private final Paint mFadePaint = new Paint(); //定义滑动缩放的值 private float mScrollScale; //定义滑动菜单的阴影 private Drawable mShadowDrawable; //定义右边滑动菜单的阴影图片 private Drawable mSecondaryShadowDrawable; //定义阴影的宽度 private int mShadowWidth; /** * 设置模式的值 */ public void setMode(int mode) { if (mode == SlidingMenu.LEFT || mode == SlidingMenu.RIGHT) { if (mContent != null) mContent.setVisibility(View.VISIBLE); if (mSecondaryContent != null) mSecondaryContent.setVisibility(View.INVISIBLE); } mMode = mode; } /** * 得到模式的值 */ public int getMode() { return mMode; } /** * 设置滑动缩放的值 */ public void setScrollScale(float scrollScale) { mScrollScale = scrollScale; } /** * 得到滑动缩放的值 */ public float getScrollScale() { return mScrollScale; } /** * 设置滑动菜单的阴影 */ public void setShadowDrawable(Drawable shadow) { mShadowDrawable = shadow; invalidate(); } /** * 设置右边滑动菜单的阴影 */ public void setSecondaryShadowDrawable(Drawable shadow) { mSecondaryShadowDrawable = shadow; invalidate(); } /** * 设置阴影的宽度 */ public void setShadowWidth(int width) { mShadowWidth = width; invalidate(); } /** * 设置能否使用渐入渐出效果 */ public void setFadeEnabled(boolean b) { mFadeEnabled = b; } /** * 设置渐入渐出的值 */ public void setFadeDegree(float degree) { if (degree > 1.0f || degree < 0.0f) throw new IllegalStateException("The BehindFadeDegree must be between 0.0f and 1.0f"); mFadeDegree = degree; } /** * 得到菜单页面 */ public int getMenuPage(int page) { page = (page > 1) ? 2 : ((page < 1) ? 0 : page); if (mMode == SlidingMenu.LEFT && page > 1) { return 0; } else if (mMode == SlidingMenu.RIGHT && page < 1) { return 2; } else { return page; } } /** * 滑动下方视图到达的位置 */ public void scrollBehindTo(View content, int x, int y) { int vis = View.VISIBLE; if (mMode == SlidingMenu.LEFT) { if (x >= content.getLeft()) vis = View.INVISIBLE; scrollTo((int)((x + getBehindWidth())*mScrollScale), y); } else if (mMode == SlidingMenu.RIGHT) { if (x <= content.getLeft()) vis = View.INVISIBLE; scrollTo((int)(getBehindWidth() - getWidth() + (x-getBehindWidth())*mScrollScale), y); } else if (mMode == SlidingMenu.LEFT_RIGHT) { mContent.setVisibility(x >= content.getLeft() ? View.INVISIBLE : View.VISIBLE); mSecondaryContent.setVisibility(x <= content.getLeft() ? View.INVISIBLE : View.VISIBLE); vis = x == 0 ? View.INVISIBLE : View.VISIBLE; if (x <= content.getLeft()) { scrollTo((int)((x + getBehindWidth())*mScrollScale), y); } else { scrollTo((int)(getBehindWidth() - getWidth() + (x-getBehindWidth())*mScrollScale), y); } } if (vis == View.INVISIBLE) Log.v(TAG, "behind INVISIBLE"); setVisibility(vis); } /** * 得到左边菜单的视图 */ public int getMenuLeft(View content, int page) { if (mMode == SlidingMenu.LEFT) { switch (page) { case 0: return content.getLeft() - getBehindWidth(); case 2: return content.getLeft(); } } else if (mMode == SlidingMenu.RIGHT) { switch (page) { case 0: return content.getLeft(); case 2: return content.getLeft() + getBehindWidth(); } } else if (mMode == SlidingMenu.LEFT_RIGHT) { switch (page) { case 0: return content.getLeft() - getBehindWidth(); case 2: return content.getLeft() + getBehindWidth(); } } return content.getLeft(); } /** * 得到左边框视图 */ public int getAbsLeftBound(View content) { if (mMode == SlidingMenu.LEFT || mMode == SlidingMenu.LEFT_RIGHT) { return content.getLeft() - getBehindWidth(); } else if (mMode == SlidingMenu.RIGHT) { return content.getLeft(); } return 0; } /** * 得到右边框视图 */ public int getAbsRightBound(View content) { if (mMode == SlidingMenu.LEFT) { return content.getLeft(); } else if (mMode == SlidingMenu.RIGHT || mMode == SlidingMenu.LEFT_RIGHT) { return content.getLeft() + getBehindWidth(); } return 0; } /** * 是否允许触摸屏幕的边缘 */ public boolean marginTouchAllowed(View content, int x) { int left = content.getLeft(); int right = content.getRight(); if (mMode == SlidingMenu.LEFT) { return (x >= left && x <= mMarginThreshold + left); } else if (mMode == SlidingMenu.RIGHT) { return (x <= right && x >= right - mMarginThreshold); } else if (mMode == SlidingMenu.LEFT_RIGHT) { return (x >= left && x <= mMarginThreshold + left) || (x <= right && x >= right - mMarginThreshold); } return false; } /** * 设置触摸模式的值 */ public void setTouchMode(int i) { mTouchMode = i; } /** * 是否允许通过触摸打开滑动菜单 */ public boolean menuOpenTouchAllowed(View content, int currPage, float x) { switch (mTouchMode) { case SlidingMenu.TOUCHMODE_FULLSCREEN: return true; case SlidingMenu.TOUCHMODE_MARGIN: return menuTouchInQuickReturn(content, currPage, x); } return false; } /** * 滑动菜单快速返回 */ public boolean menuTouchInQuickReturn(View content, int currPage, float x) { if (mMode == SlidingMenu.LEFT || (mMode == SlidingMenu.LEFT_RIGHT && currPage == 0)) { return x >= content.getLeft(); } else if (mMode == SlidingMenu.RIGHT || (mMode == SlidingMenu.LEFT_RIGHT && currPage == 2)) { return x <= content.getRight(); } return false; } /** * 是否允许关闭滑动菜单 */ public boolean menuClosedSlideAllowed(float dx) { if (mMode == SlidingMenu.LEFT) { return dx > 0; } else if (mMode == SlidingMenu.RIGHT) { return dx < 0; } else if (mMode == SlidingMenu.LEFT_RIGHT) { return true; } return false; } /** * 是否允许打开滑动菜单 */ public boolean menuOpenSlideAllowed(float dx) { if (mMode == SlidingMenu.LEFT) { return dx < 0; } else if (mMode == SlidingMenu.RIGHT) { return dx > 0; } else if (mMode == SlidingMenu.LEFT_RIGHT) { return true; } return false; } /** * 画滑动菜单的阴影 */ public void drawShadow(View content, Canvas canvas) { if (mShadowDrawable == null || mShadowWidth <= 0) return; int left = 0; if (mMode == SlidingMenu.LEFT) { left = content.getLeft() - mShadowWidth; } else if (mMode == SlidingMenu.RIGHT) { left = content.getRight(); } else if (mMode == SlidingMenu.LEFT_RIGHT) { if (mSecondaryShadowDrawable != null) { left = content.getRight(); mSecondaryShadowDrawable.setBounds(left, 0, left + mShadowWidth, getHeight()); mSecondaryShadowDrawable.draw(canvas); } left = content.getLeft() - mShadowWidth; } mShadowDrawable.setBounds(left, 0, left + mShadowWidth, getHeight()); mShadowDrawable.draw(canvas); } /** * 画出渐入渐出效果 */ public void drawFade(View content, Canvas canvas, float openPercent) { if (!mFadeEnabled) return; final int alpha = (int) (mFadeDegree * 255 * Math.abs(1-openPercent)); mFadePaint.setColor(Color.argb(alpha, 0, 0, 0)); int left = 0; int right = 0; if (mMode == SlidingMenu.LEFT) { left = content.getLeft() - getBehindWidth(); right = content.getLeft(); } else if (mMode == SlidingMenu.RIGHT) { left = content.getRight(); right = content.getRight() + getBehindWidth(); } else if (mMode == SlidingMenu.LEFT_RIGHT) { left = content.getLeft() - getBehindWidth(); right = content.getLeft(); canvas.drawRect(left, 0, right, getHeight(), mFadePaint); left = content.getRight(); right = content.getRight() + getBehindWidth(); } canvas.drawRect(left, 0, right, getHeight(), mFadePaint); } private boolean mSelectorEnabled = true; private Bitmap mSelectorDrawable; private View mSelectedView; public void drawSelector(View content, Canvas canvas, float openPercent) { if (!mSelectorEnabled) return; if (mSelectorDrawable != null && mSelectedView != null) { String tag = (String) mSelectedView.getTag(R.id.selected_view); if (tag.equals(TAG+"SelectedView")) { canvas.save(); int left, right, offset; offset = (int) (mSelectorDrawable.getWidth() * openPercent); if (mMode == SlidingMenu.LEFT) { right = content.getLeft(); left = right - offset; canvas.clipRect(left, 0, right, getHeight()); canvas.drawBitmap(mSelectorDrawable, left, getSelectorTop(), null); } else if (mMode == SlidingMenu.RIGHT) { left = content.getRight(); right = left + offset; canvas.clipRect(left, 0, right, getHeight()); canvas.drawBitmap(mSelectorDrawable, right - mSelectorDrawable.getWidth(), getSelectorTop(), null); } canvas.restore(); } } } public void setSelectorEnabled(boolean b) { mSelectorEnabled = b; } public void setSelectedView(View v) { if (mSelectedView != null) { mSelectedView.setTag(R.id.selected_view, null); mSelectedView = null; } if (v != null && v.getParent() != null) { mSelectedView = v; mSelectedView.setTag(R.id.selected_view, TAG+"SelectedView"); invalidate(); } } private int getSelectorTop() { int y = mSelectedView.getTop(); y += (mSelectedView.getHeight() - mSelectorDrawable.getHeight()) / 2; return y; } public void setSelectorBitmap(Bitmap b) { mSelectorDrawable = b; refreshDrawableState(); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/slidingmenu/SlidingActivityBase.java ================================================ package com.hankkin.compustrading.slidingmenu; import android.view.View; import android.view.ViewGroup.LayoutParams; public interface SlidingActivityBase { /** * Set the behind view content to an explicit view. This view is placed directly into the behind view 's view hierarchy. * It can itself be a complex view hierarchy. * * @param view The desired content to display. * @param layoutParams Layout parameters for the view. */ public void setBehindContentView(View view, LayoutParams layoutParams); /** * Set the behind view content to an explicit view. This view is placed directly into the behind view 's view hierarchy. * It can itself be a complex view hierarchy. When calling this method, the layout parameters of the specified * view are ignored. Both the width and the height of the view are set by default to MATCH_PARENT. To use your * own layout parameters, invoke setContentView(android.view.View, android.view.ViewGroup.LayoutParams) instead. * * @param view The desired content to display. */ public void setBehindContentView(View view); /** * Set the behind view content from a layout resource. The resource will be inflated, adding all top-level views * to the behind view. * * @param layoutResID Resource ID to be inflated. */ public void setBehindContentView(int layoutResID); /** * Gets the SlidingMenu associated with this activity. * * @return the SlidingMenu associated with this activity. */ public SlidingMenu getSlidingMenu(); /** * Toggle the SlidingMenu. If it is open, it will be closed, and vice versa. */ public void toggle(); /** * Close the SlidingMenu and show the content view. */ public void showContent(); /** * Open the SlidingMenu and show the menu view. */ public void showMenu(); /** * Open the SlidingMenu and show the secondary (right) menu view. Will default to the regular menu * if there is only one. */ public void showSecondaryMenu(); /** * Controls whether the ActionBar slides along with the above view when the menu is opened, * or if it stays in place. * * @param slidingActionBarEnabled True if you want the ActionBar to slide along with the SlidingMenu, * false if you want the ActionBar to stay in place */ public void setSlidingActionBarEnabled(boolean slidingActionBarEnabled); } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/slidingmenu/SlidingActivityHelper.java ================================================ package com.hankkin.compustrading.slidingmenu; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup.LayoutParams; import com.hankkin.compustrading.R; public class SlidingActivityHelper { private Activity mActivity; private SlidingMenu mSlidingMenu; private View mViewAbove; private View mViewBehind; private boolean mBroadcasting = false; private boolean mOnPostCreateCalled = false; private boolean mEnableSlide = true; /** * Instantiates a new SlidingActivityHelper. * * @param activity the associated activity */ public SlidingActivityHelper(Activity activity) { mActivity = activity; } /** * Sets mSlidingMenu as a newly inflated SlidingMenu. Should be called within the activitiy's onCreate() * * @param savedInstanceState the saved instance state (unused) */ public void onCreate(Bundle savedInstanceState) { mSlidingMenu = (SlidingMenu) LayoutInflater.from(mActivity).inflate(R.layout.slidingmenumain, null); } /** * Further SlidingMenu initialization. Should be called within the activitiy's onPostCreate() * * @param savedInstanceState the saved instance state (unused) */ public void onPostCreate(Bundle savedInstanceState) { if (mViewBehind == null || mViewAbove == null) { throw new IllegalStateException("Both setBehindContentView must be called " + "in onCreate in addition to setContentView."); } mOnPostCreateCalled = true; mSlidingMenu.attachToActivity(mActivity, mEnableSlide ? SlidingMenu.SLIDING_WINDOW : SlidingMenu.SLIDING_CONTENT); final boolean open; final boolean secondary; if (savedInstanceState != null) { open = savedInstanceState.getBoolean("SlidingActivityHelper.open"); secondary = savedInstanceState.getBoolean("SlidingActivityHelper.secondary"); } else { open = false; secondary = false; } new Handler().post(new Runnable() { public void run() { if (open) { if (secondary) { mSlidingMenu.showSecondaryMenu(false); } else { mSlidingMenu.showMenu(false); } } else { mSlidingMenu.showContent(false); } } }); } /** * Controls whether the ActionBar slides along with the above view when the menu is opened, * or if it stays in place. * * @param slidingActionBarEnabled True if you want the ActionBar to slide along with the SlidingMenu, * false if you want the ActionBar to stay in place */ public void setSlidingActionBarEnabled(boolean slidingActionBarEnabled) { if (mOnPostCreateCalled) throw new IllegalStateException("enableSlidingActionBar must be called in onCreate."); mEnableSlide = slidingActionBarEnabled; } /** * Finds a view that was identified by the id attribute from the XML that was processed in onCreate(Bundle). * * @param id the resource id of the desired view * @return The view if found or null otherwise. */ public View findViewById(int id) { View v; if (mSlidingMenu != null) { v = mSlidingMenu.findViewById(id); if (v != null) return v; } return null; } /** * Called to retrieve per-instance state from an activity before being killed so that the state can be * restored in onCreate(Bundle) or onRestoreInstanceState(Bundle) (the Bundle populated by this method * will be passed to both). * * @param outState Bundle in which to place your saved state. */ public void onSaveInstanceState(Bundle outState) { outState.putBoolean("SlidingActivityHelper.open", mSlidingMenu.isMenuShowing()); outState.putBoolean("SlidingActivityHelper.secondary", mSlidingMenu.isSecondaryMenuShowing()); } /** * Register the above content view. * * @param v the above content view to register * @param params LayoutParams for that view (unused) */ public void registerAboveContentView(View v, LayoutParams params) { if (!mBroadcasting) mViewAbove = v; } /** * Set the activity content to an explicit view. This view is placed directly into the activity's view * hierarchy. It can itself be a complex view hierarchy. When calling this method, the layout parameters * of the specified view are ignored. Both the width and the height of the view are set by default to * MATCH_PARENT. To use your own layout parameters, invoke setContentView(android.view.View, * android.view.ViewGroup.LayoutParams) instead. * * @param v The desired content to display. */ public void setContentView(View v) { mBroadcasting = true; mActivity.setContentView(v); } /** * Set the behind view content to an explicit view. This view is placed directly into the behind view 's view hierarchy. * It can itself be a complex view hierarchy. * * @param view The desired content to display. * @param layoutParams Layout parameters for the view. (unused) */ public void setBehindContentView(View view, LayoutParams layoutParams) { mViewBehind = view; mSlidingMenu.setMenu(mViewBehind); } /** * Gets the SlidingMenu associated with this activity. * * @return the SlidingMenu associated with this activity. */ public SlidingMenu getSlidingMenu() { return mSlidingMenu; } /** * Toggle the SlidingMenu. If it is open, it will be closed, and vice versa. */ public void toggle() { mSlidingMenu.toggle(); } /** * Close the SlidingMenu and show the content view. */ public void showContent() { mSlidingMenu.showContent(); } /** * Open the SlidingMenu and show the menu view. */ public void showMenu() { mSlidingMenu.showMenu(); } /** * Open the SlidingMenu and show the secondary menu view. Will default to the regular menu * if there is only one. */ public void showSecondaryMenu() { mSlidingMenu.showSecondaryMenu(); } /** * On key up. * * @param keyCode the key code * @param event the event * @return true, if successful */ public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && mSlidingMenu.isMenuShowing()) { showContent(); return true; } return false; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/slidingmenu/SlidingMenu.java ================================================ package com.hankkin.compustrading.slidingmenu; import java.lang.reflect.Method; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.RelativeLayout; import com.hankkin.compustrading.R; public class SlidingMenu extends RelativeLayout { private static final String TAG = "SlidingMenu"; public static final int SLIDING_WINDOW = 0; public static final int SLIDING_CONTENT = 1; private boolean mActionbarOverlay = false; /** * 为setTouchModeAbove()方法设置一个常量值,允许滑动菜单通过滑动屏幕的边缘被打开 */ public static final int TOUCHMODE_MARGIN = 0; /** * 为setTouchModeAbove()方法设置一个常量值,允许滑动菜单通过滑动屏幕的任何地方被打开 */ public static final int TOUCHMODE_FULLSCREEN = 1; /** * 为setTouchModeAbove()方法设置一个常量值,不允许滑动菜单通过滑动屏幕被打开 */ public static final int TOUCHMODE_NONE = 2; /** * 为setMode()方法设置一个常量值,把滑动菜单放在左边 */ public static final int LEFT = 0; /** * 为setMode()方法设置一个常量值,把滑动菜单放在右边 */ public static final int RIGHT = 1; /** * 为setMode()方法设置一个常量值,把滑动菜单放在左右两边 */ public static final int LEFT_RIGHT = 2; /** * 定义上方视图对象 */ private CustomViewAbove mViewAbove; /** * 定义下方视图对象 */ private CustomViewBehind mViewBehind; /** * 定义滑动菜单打开的监听对象 */ private OnOpenListener mOpenListener; /** * 定义滑动菜单关闭的监听对象 */ private OnCloseListener mCloseListener; /** * 滑动菜单打开时的监听事件 */ public interface OnOpenListener { public void onOpen(); } /** * 监测滑动菜单是否已经打开的监听事件 */ public interface OnOpenedListener { public void onOpened(); } /** * 滑动菜单关闭时的监听事件 */ public interface OnCloseListener { public void onClose(); } /** * 监测滑动菜单是否已经关闭的监听事件 */ public interface OnClosedListener { public void onClosed(); } /** * The Interface CanvasTransformer. */ public interface CanvasTransformer { /** * Transform canvas. * * @param canvas the canvas * @param percentOpen the percent open */ public void transformCanvas(Canvas canvas, float percentOpen); } /** * 初始化滑动菜单 * * @param context the associated Context */ public SlidingMenu(Context context) { this(context, null); } /** * 初始化滑动菜单 * * @param activity the activity to attach slidingmenu * @param slideStyle the slidingmenu style */ public SlidingMenu(Activity activity, int slideStyle) { this(activity, null); this.attachToActivity(activity, slideStyle); } /** * 初始化滑动菜单 * * @param context the associated Context * @param attrs the attrs */ public SlidingMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } /** * 初始化滑动菜单 * * @param context the associated Context * @param attrs the attrs * @param defStyle the def style */ public SlidingMenu(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); LayoutParams behindParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mViewBehind = new CustomViewBehind(context); addView(mViewBehind, behindParams); LayoutParams aboveParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mViewAbove = new CustomViewAbove(context); addView(mViewAbove, aboveParams); // register the CustomViewBehind with the CustomViewAbove mViewAbove.setCustomViewBehind(mViewBehind); mViewBehind.setCustomViewAbove(mViewAbove); mViewAbove.setOnPageChangeListener(new CustomViewAbove.OnPageChangeListener() { public static final int POSITION_OPEN = 0; public static final int POSITION_CLOSE = 1; public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } public void onPageSelected(int position) { if (position == POSITION_OPEN && mOpenListener != null) { mOpenListener.onOpen(); } else if (position == POSITION_CLOSE && mCloseListener != null) { mCloseListener.onClose(); } } }); // now style everything! TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingMenu); // set the above and behind views if defined in xml int mode = ta.getInt(R.styleable.SlidingMenu_mode, LEFT); setMode(mode); int viewAbove = ta.getResourceId(R.styleable.SlidingMenu_viewAbove, -1); if (viewAbove != -1) { setContent(viewAbove); } else { setContent(new FrameLayout(context)); } int viewBehind = ta.getResourceId(R.styleable.SlidingMenu_viewBehind, -1); if (viewBehind != -1) { setMenu(viewBehind); } else { setMenu(new FrameLayout(context)); } int touchModeAbove = ta.getInt(R.styleable.SlidingMenu_touchModeAbove, TOUCHMODE_MARGIN); setTouchModeAbove(touchModeAbove); int touchModeBehind = ta.getInt(R.styleable.SlidingMenu_touchModeBehind, TOUCHMODE_MARGIN); setTouchModeBehind(touchModeBehind); int offsetBehind = (int) ta.getDimension(R.styleable.SlidingMenu_behindOffset, -1); int widthBehind = (int) ta.getDimension(R.styleable.SlidingMenu_behindWidth, -1); if (offsetBehind != -1 && widthBehind != -1) throw new IllegalStateException("Cannot set both behindOffset and behindWidth for a SlidingMenu"); else if (offsetBehind != -1) setBehindOffset(offsetBehind); else if (widthBehind != -1) setBehindWidth(widthBehind); else setBehindOffset(0); float scrollOffsetBehind = ta.getFloat(R.styleable.SlidingMenu_behindScrollScale, 0.33f); setBehindScrollScale(scrollOffsetBehind); int shadowRes = ta.getResourceId(R.styleable.SlidingMenu_shadowDrawable, -1); if (shadowRes != -1) { setShadowDrawable(shadowRes); } int shadowWidth = (int) ta.getDimension(R.styleable.SlidingMenu_shadowWidth, 0); setShadowWidth(shadowWidth); boolean fadeEnabled = ta.getBoolean(R.styleable.SlidingMenu_fadeEnabled, true); setFadeEnabled(fadeEnabled); float fadeDeg = ta.getFloat(R.styleable.SlidingMenu_fadeDegree, 0.33f); setFadeDegree(fadeDeg); boolean selectorEnabled = ta.getBoolean(R.styleable.SlidingMenu_selectorEnabled, false); setSelectorEnabled(selectorEnabled); int selectorRes = ta.getResourceId(R.styleable.SlidingMenu_selectorDrawable, -1); if (selectorRes != -1) setSelectorDrawable(selectorRes); ta.recycle(); } /** * 把滑动菜单添加进所有的Activity中 * * @param activity the Activity * @param slideStyle either SLIDING_CONTENT or SLIDING_WINDOW */ public void attachToActivity(Activity activity, int slideStyle) { attachToActivity(activity, slideStyle, false); } /** * 把滑动菜单添加进所有的Activity中 * * @param activity the Activity * @param slideStyle either SLIDING_CONTENT or SLIDING_WINDOW * @param actionbarOverlay whether or not the ActionBar is overlaid */ public void attachToActivity(Activity activity, int slideStyle, boolean actionbarOverlay) { if (slideStyle != SLIDING_WINDOW && slideStyle != SLIDING_CONTENT) throw new IllegalArgumentException("slideStyle must be either SLIDING_WINDOW or SLIDING_CONTENT"); if (getParent() != null) throw new IllegalStateException("This SlidingMenu appears to already be attached"); // get the window background TypedArray a = activity.getTheme().obtainStyledAttributes(new int[] {android.R.attr.windowBackground}); int background = a.getResourceId(0, 0); a.recycle(); switch (slideStyle) { case SLIDING_WINDOW: mActionbarOverlay = false; ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView(); ViewGroup decorChild = (ViewGroup) decor.getChildAt(0); // save ActionBar themes that have transparent assets decorChild.setBackgroundResource(background); decor.removeView(decorChild); decor.addView(this); setContent(decorChild); break; case SLIDING_CONTENT: mActionbarOverlay = actionbarOverlay; // take the above view out of ViewGroup contentParent = (ViewGroup)activity.findViewById(android.R.id.content); View content = contentParent.getChildAt(0); contentParent.removeView(content); contentParent.addView(this); setContent(content); // save people from having transparent backgrounds if (content.getBackground() == null) content.setBackgroundResource(background); break; } } /** * 从布局资源文件中设置上方的视图内容,这个布局会被填充添加到所有图层的最上方 */ public void setContent(int res) { setContent(LayoutInflater.from(getContext()).inflate(res, null)); } /** * 通过View来设置上方的视图内容 */ public void setContent(View view) { mViewAbove.setContent(view); showContent(); } /** * 得到上方的视图内容 */ public View getContent() { return mViewAbove.getContent(); } /** * 从布局资源文件中设置下方(滑动菜单)的视图内容,这个布局会被填充添加到所有图层的最下方 * * @param res the new content */ public void setMenu(int res) { setMenu(LayoutInflater.from(getContext()).inflate(res, null)); } /** * 得到下方(滑动菜单)的视图内容 * * @param The desired content to display. */ public void setMenu(View v) { mViewBehind.setContent(v); } /** * 得到下方(滑动菜单)的视图内容 */ public View getMenu() { return mViewBehind.getContent(); } /** * 从布局资源文件中设置下方(右边滑动菜单)的视图内容,这个布局会被填充添加到所有图层的最下方 */ public void setSecondaryMenu(int res) { setSecondaryMenu(LayoutInflater.from(getContext()).inflate(res, null)); } /** * 设置下方(右边滑动菜单)的视图内容 */ public void setSecondaryMenu(View v) { mViewBehind.setSecondaryContent(v); } /** * 得到下方(右边滑动菜单)的视图内容 */ public View getSecondaryMenu() { return mViewBehind.getSecondaryContent(); } /** * 设置上方视图是否能够滑动 */ public void setSlidingEnabled(boolean b) { mViewAbove.setSlidingEnabled(b); } /** * 检测上方视图是否能够滑动 */ public boolean isSlidingEnabled() { return mViewAbove.isSlidingEnabled(); } /** * 设置滑动菜单出现在视图中的位置 * * @param mode must be either SlidingMenu.LEFT or SlidingMenu.RIGHT */ public void setMode(int mode) { if (mode != LEFT && mode != RIGHT && mode != LEFT_RIGHT) { throw new IllegalStateException("SlidingMenu mode must be LEFT, RIGHT, or LEFT_RIGHT"); } mViewBehind.setMode(mode); } /** * 得到滑动菜单在视图中的位置 * * @return the current mode, either SlidingMenu.LEFT or SlidingMenu.RIGHT */ public int getMode() { return mViewBehind.getMode(); } /** * 设置滑动菜单是否是静态模式(不能够使用滑动菜单) */ public void setStatic(boolean b) { if (b) { setSlidingEnabled(false); mViewAbove.setCustomViewBehind(null); mViewAbove.setCurrentItem(1); // mViewBehind.setCurrentItem(0); } else { mViewAbove.setCurrentItem(1); // mViewBehind.setCurrentItem(1); mViewAbove.setCustomViewBehind(mViewBehind); setSlidingEnabled(true); } } /** * 打开滑动菜单并显示菜单的视图 */ public void showMenu() { showMenu(true); } /** * 是否使用动画效果打开滑动菜单并显示菜单的视图 */ public void showMenu(boolean animate) { mViewAbove.setCurrentItem(0, animate); } /** * 打开右边的滑动菜单并显示菜单的视图 */ public void showSecondaryMenu() { showSecondaryMenu(true); } /** * 是否使用动画效果打开右边的滑动菜单并显示菜单的视图 */ public void showSecondaryMenu(boolean animate) { mViewAbove.setCurrentItem(2, animate); } /** * 关闭菜单并显示上方的视图 */ public void showContent() { showContent(true); } /** * 是否使用动画效果关闭菜单并显示上方的视图 */ public void showContent(boolean animate) { mViewAbove.setCurrentItem(1, animate); } /** * 滑动菜单的开关 */ public void toggle() { toggle(true); } /** * 是否使用动画效果打开或关闭滑动菜单 */ public void toggle(boolean animate) { if (isMenuShowing()) { showContent(animate); } else { showMenu(animate); } } /** * 检测滑动菜单是否正在被显示 */ public boolean isMenuShowing() { return mViewAbove.getCurrentItem() == 0 || mViewAbove.getCurrentItem() == 2; } /** * 检测右边滑动菜单是否正在被显示 */ public boolean isSecondaryMenuShowing() { return mViewAbove.getCurrentItem() == 2; } /** * 得到下方视图的偏移量 */ public int getBehindOffset() { return ((RelativeLayout.LayoutParams)mViewBehind.getLayoutParams()).rightMargin; } /** * 根据像素的值来设置下方视图的偏移量 * * @param i The margin, in pixels, on the right of the screen that the behind view scrolls to. */ public void setBehindOffset(int i) { mViewBehind.setWidthOffset(i); } /** * 根据dimension资源文件的ID来设置下方视图的偏移量 * * @param resID The dimension resource id to be set as the behind offset. * The menu, when open, will leave this width margin on the right of the screen. */ public void setBehindOffsetRes(int resID) { int i = (int) getContext().getResources().getDimension(resID); setBehindOffset(i); } /** * 根据像素的值来设置上方视图的偏移量 * * @param i the new above offset, in pixels */ public void setAboveOffset(int i) { mViewAbove.setAboveOffset(i); } /** * 根据dimension资源文件的ID来设置上方视图的偏移量 * * @param resID The dimension resource id to be set as the above offset. */ public void setAboveOffsetRes(int resID) { int i = (int) getContext().getResources().getDimension(resID); setAboveOffset(i); } /** * 根据像素的值来设置下方视图的宽度 * * @param i The width the Sliding Menu will open to, in pixels */ public void setBehindWidth(int i) { int width; Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); try { Class cls = Display.class; Class[] parameterTypes = {Point.class}; Point parameter = new Point(); Method method = cls.getMethod("getSize", parameterTypes); method.invoke(display, parameter); width = parameter.x; } catch (Exception e) { width = display.getWidth(); } setBehindOffset(width-i); } /** * 根据dimension资源文件的ID来设置下方视图的宽度 * * @param res The dimension resource id to be set as the behind width offset. * The menu, when open, will open this wide. */ public void setBehindWidthRes(int res) { int i = (int) getContext().getResources().getDimension(res); setBehindWidth(i); } /** * 得到下方视图的在滚动时的缩放比例 * * @return The scale of the parallax scroll */ public float getBehindScrollScale() { return mViewBehind.getScrollScale(); } /** * 设置下方视图的在滚动时的缩放比例 * * @param f The scale of the parallax scroll (i.e. 1.0f scrolls 1 pixel for every * 1 pixel that the above view scrolls and 0.0f scrolls 0 pixels) */ public void setBehindScrollScale(float f) { if (f < 0 && f > 1) throw new IllegalStateException("ScrollScale must be between 0 and 1"); mViewBehind.setScrollScale(f); } /** * 得到边缘触摸的临界值 */ public int getTouchmodeMarginThreshold() { return mViewBehind.getMarginThreshold(); } /** * 当触摸的的模式为边缘触摸时,设置边缘触摸的临界值 */ public void setTouchmodeMarginThreshold(int touchmodeMarginThreshold) { mViewBehind.setMarginThreshold(touchmodeMarginThreshold); } /** * Sets the behind canvas transformer. * * @param t the new behind canvas transformer */ public void setBehindCanvasTransformer(CanvasTransformer t) { mViewBehind.setCanvasTransformer(t); } /** * 得到上方视图的触摸模式的值 */ public int getTouchModeAbove() { return mViewAbove.getTouchMode(); } /** * 设置上方视图的触摸模式的值 */ public void setTouchModeAbove(int i) { if (i != TOUCHMODE_FULLSCREEN && i != TOUCHMODE_MARGIN && i != TOUCHMODE_NONE) { throw new IllegalStateException("TouchMode must be set to either" + "TOUCHMODE_FULLSCREEN or TOUCHMODE_MARGIN or TOUCHMODE_NONE."); } mViewAbove.setTouchMode(i); } /** * 设置下方视图的触摸模式的值 */ public void setTouchModeBehind(int i) { if (i != TOUCHMODE_FULLSCREEN && i != TOUCHMODE_MARGIN && i != TOUCHMODE_NONE) { throw new IllegalStateException("TouchMode must be set to either" + "TOUCHMODE_FULLSCREEN or TOUCHMODE_MARGIN or TOUCHMODE_NONE."); } mViewBehind.setTouchMode(i); } /** * 根据资源文件ID来设置滑动菜单的阴影效果 * * @param resId the resource ID of the new shadow drawable */ public void setShadowDrawable(int resId) { setShadowDrawable(getContext().getResources().getDrawable(resId)); } /** * 根据Drawable来设置滑动菜单的阴影效果 * * @param d the new shadow drawable */ public void setShadowDrawable(Drawable d) { mViewBehind.setShadowDrawable(d); } /** * 根据资源文件ID来设置右边滑动菜单的阴影效果 * * @param resId the resource ID of the new shadow drawable */ public void setSecondaryShadowDrawable(int resId) { setSecondaryShadowDrawable(getContext().getResources().getDrawable(resId)); } /** * 根据Drawable来设置滑动菜单的阴影效果 * * @param d the new shadow drawable */ public void setSecondaryShadowDrawable(Drawable d) { mViewBehind.setSecondaryShadowDrawable(d); } /** * 根据dimension资源文件的ID来设置阴影的宽度 * * @param resId The dimension resource id to be set as the shadow width. */ public void setShadowWidthRes(int resId) { setShadowWidth((int)getResources().getDimension(resId)); } /** * 根据像素的值来设置阴影的宽度 * * @param pixels the new shadow width, in pixels */ public void setShadowWidth(int pixels) { mViewBehind.setShadowWidth(pixels); } /** * 设置是否能够使用滑动菜单渐入渐出的效果 **/ public void setFadeEnabled(boolean b) { mViewBehind.setFadeEnabled(b); } /** * 设置渐入渐出效果的值 * * @param f the new fade degree, between 0.0f and 1.0f */ public void setFadeDegree(float f) { mViewBehind.setFadeDegree(f); } /** * Enables or disables whether the selector is drawn * * @param b true to draw the selector, false to not draw the selector */ public void setSelectorEnabled(boolean b) { mViewBehind.setSelectorEnabled(true); } /** * Sets the selected view. The selector will be drawn here * * @param v the new selected view */ public void setSelectedView(View v) { mViewBehind.setSelectedView(v); } /** * Sets the selector drawable. * * @param res a resource ID for the selector drawable */ public void setSelectorDrawable(int res) { mViewBehind.setSelectorBitmap(BitmapFactory.decodeResource(getResources(), res)); } /** * Sets the selector drawable. * * @param b the new selector bitmap */ public void setSelectorBitmap(Bitmap b) { mViewBehind.setSelectorBitmap(b); } /** * 添加被忽略的视图 */ public void addIgnoredView(View v) { mViewAbove.addIgnoredView(v); } /** * 移除被忽略的视图 */ public void removeIgnoredView(View v) { mViewAbove.removeIgnoredView(v); } /** * 当模式为Fullscreen模式时,触摸屏幕清除所有被忽略的视图 */ public void clearIgnoredViews() { mViewAbove.clearIgnoredViews(); } /** * 设置打开监听事件,当滑动菜单被打开时调用 */ public void setOnOpenListener(OnOpenListener listener) { mOpenListener = listener; } /** * 设置关闭监听事件,当滑动菜单被关闭时调用 */ public void setOnCloseListener(OnCloseListener listener) { //mViewAbove.setOnCloseListener(listener); mCloseListener = listener; } /** * 设置打开监听事件,当滑动菜单被打开过之后调用 */ public void setOnOpenedListener(OnOpenedListener listener) { mViewAbove.setOnOpenedListener(listener); } /** * 设置关闭监听事件,当滑动菜单被关闭过之后调用 */ public void setOnClosedListener(OnClosedListener listener) { mViewAbove.setOnClosedListener(listener); } /** * 功能描述:保存状态的类,继承自BaseSavedState */ public static class SavedState extends BaseSavedState { private final int mItem; public SavedState(Parcelable superState, int item) { super(superState); mItem = item; } private SavedState(Parcel in) { super(in); mItem = in.readInt(); } public int getItem() { return mItem; } /* (non-Javadoc) * @see android.view.AbsSavedState#writeToParcel(android.os.Parcel, int) */ public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(mItem); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } /* (non-Javadoc) * @see android.view.View#onSaveInstanceState() */ @Override protected Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState, mViewAbove.getCurrentItem()); return ss; } /* (non-Javadoc) * @see android.view.View#onRestoreInstanceState(android.os.Parcelable) */ @Override protected void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState)state; super.onRestoreInstanceState(ss.getSuperState()); mViewAbove.setCurrentItem(ss.getItem()); } /* (non-Javadoc) * @see android.view.ViewGroup#fitSystemWindows(android.graphics.Rect) */ @SuppressLint("NewApi") @Override protected boolean fitSystemWindows(Rect insets) { int leftPadding = insets.left; int rightPadding = insets.right; int topPadding = insets.top; int bottomPadding = insets.bottom; if (!mActionbarOverlay) { Log.v(TAG, "setting padding!"); setPadding(leftPadding, topPadding, rightPadding, bottomPadding); } return true; } private Handler mHandler = new Handler(); @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void manageLayers(float percentOpen) { if (Build.VERSION.SDK_INT < 11) return; boolean layer = percentOpen > 0.0f && percentOpen < 1.0f; final int layerType = layer ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE; if (layerType != getContent().getLayerType()) { mHandler.post(new Runnable() { public void run() { Log.v(TAG, "changing layerType. hardware? " + (layerType == View.LAYER_TYPE_HARDWARE)); getContent().setLayerType(layerType, null); getMenu().setLayerType(layerType, null); if (getSecondaryMenu() != null) { getSecondaryMenu().setLayerType(layerType, null); } } }); } } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/slidingmenu/SlidingPreferenceActivity.java ================================================ package com.hankkin.compustrading.slidingmenu; import android.os.Bundle; import android.preference.PreferenceActivity; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup.LayoutParams; public class SlidingPreferenceActivity extends PreferenceActivity implements SlidingActivityBase { private SlidingActivityHelper mHelper; /* (non-Javadoc) * @see android.app.Activity#onCreate(android.os.Bundle) */ @Override public void onCreate(Bundle savedInstanceState) { mHelper = new SlidingActivityHelper(this); super.onCreate(savedInstanceState); mHelper.onCreate(savedInstanceState); } /* (non-Javadoc) * @see android.app.Activity#onPostCreate(android.os.Bundle) */ @Override public void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); mHelper.onPostCreate(savedInstanceState); } /* (non-Javadoc) * @see android.app.Activity#findViewById(int) */ @Override public View findViewById(int id) { View v = super.findViewById(id); if (v != null) return v; return mHelper.findViewById(id); } /* (non-Javadoc) * @see android.app.Activity#onSaveInstanceState(android.os.Bundle) */ @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mHelper.onSaveInstanceState(outState); } /* (non-Javadoc) * @see android.app.Activity#setContentView(int) */ @Override public void setContentView(int id) { setContentView(getLayoutInflater().inflate(id, null)); } /* (non-Javadoc) * @see android.app.Activity#setContentView(android.view.View) */ @Override public void setContentView(View v) { setContentView(v, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); } /* (non-Javadoc) * @see android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams) */ @Override public void setContentView(View v, LayoutParams params) { super.setContentView(v, params); mHelper.registerAboveContentView(v, params); } /* (non-Javadoc) * @see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#setBehindContentView(int) */ public void setBehindContentView(int id) { setBehindContentView(getLayoutInflater().inflate(id, null)); } /* (non-Javadoc) * @see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#setBehindContentView(android.view.View) */ public void setBehindContentView(View v) { setBehindContentView(v, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); } /* (non-Javadoc) * @see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#setBehindContentView(android.view.View, android.view.ViewGroup.LayoutParams) */ public void setBehindContentView(View v, LayoutParams params) { mHelper.setBehindContentView(v, params); } /* (non-Javadoc) * @see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#getSlidingMenu() */ public SlidingMenu getSlidingMenu() { return mHelper.getSlidingMenu(); } /* (non-Javadoc) * @see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#toggle() */ public void toggle() { mHelper.toggle(); } /* (non-Javadoc) * @see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#showAbove() */ public void showContent() { mHelper.showContent(); } /* (non-Javadoc) * @see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#showBehind() */ public void showMenu() { mHelper.showMenu(); } /* (non-Javadoc) * @see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#showSecondaryMenu() */ public void showSecondaryMenu() { mHelper.showSecondaryMenu(); } /* (non-Javadoc) * @see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#setSlidingActionBarEnabled(boolean) */ public void setSlidingActionBarEnabled(boolean b) { mHelper.setSlidingActionBarEnabled(b); } /* (non-Javadoc) * @see android.app.Activity#onKeyUp(int, android.view.KeyEvent) */ @Override public boolean onKeyUp(int keyCode, KeyEvent event) { boolean b = mHelper.onKeyUp(keyCode, event); if (b) return b; return super.onKeyUp(keyCode, event); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/CollapsingAvatarToolbar.java ================================================ package com.hankkin.compustrading.view; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.support.annotation.NonNull; import android.support.design.widget.AppBarLayout; import android.support.v7.widget.Toolbar; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.widget.LinearLayout; import android.widget.TextView; import com.hankkin.compustrading.R; import com.hankkin.compustrading.Utils.HankkinUtils; public class CollapsingAvatarToolbar extends LinearLayout implements AppBarLayout.OnOffsetChangedListener { private View avatarView; private TextView titleView; private float collapsedPadding; private float expandedPadding; private float expandedImageSize; private float collapsedImageSize; private float collapsedTextSize; private float expandedTextSize; private boolean valuesCalculatedAlready = false; private Toolbar toolbar; private AppBarLayout appBarLayout; private float collapsedHeight; private float expandedHeight; private float maxOffset; private int width; private boolean isFirst = true; private Context activity; public CollapsingAvatarToolbar(Context context) { this(context, null); init(); } public CollapsingAvatarToolbar(Context context, AttributeSet attrs) { super(context, attrs); init(); this.activity = context; width = HankkinUtils.getScreenWidth(context); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CollapsingAvatarToolbar, 0, 0); try { collapsedPadding = a.getDimension(R.styleable.CollapsingAvatarToolbar_collapsedPadding, -1); expandedPadding = a.getDimension(R.styleable.CollapsingAvatarToolbar_expandedPadding, -1); collapsedImageSize = a.getDimension(R.styleable.CollapsingAvatarToolbar_collapsedImageSize, -1); expandedImageSize = a.getDimension(R.styleable.CollapsingAvatarToolbar_expandedImageSize, -1); collapsedTextSize = a.getDimension(R.styleable.CollapsingAvatarToolbar_collapsedTextSize, -1); expandedTextSize = a.getDimension(R.styleable.CollapsingAvatarToolbar_expandedTextSize, -1); } finally { a.recycle(); } final Resources resources = getResources(); if (collapsedPadding < 0) { collapsedPadding = resources.getDimension(R.dimen.default_collapsed_padding); } if (expandedPadding < 0) { expandedPadding = resources.getDimension(R.dimen.default_expanded_padding); } if (collapsedImageSize < 0) { collapsedImageSize = resources.getDimension(R.dimen.default_collapsed_image_size); } if (expandedImageSize < 0) { expandedImageSize = resources.getDimension(R.dimen.default_expanded_image_size); } if (collapsedTextSize < 0) { collapsedTextSize = resources.getDimension(R.dimen.default_collapsed_text_size); } if (expandedTextSize < 0) { expandedTextSize = resources.getDimension(R.dimen.default_expanded_text_size); } } private void init() { setOrientation(HORIZONTAL); setGravity(Gravity.CENTER_HORIZONTAL); } @NonNull private AppBarLayout findParentAppBarLayout() { ViewParent parent = this.getParent(); if (parent instanceof AppBarLayout) { return ((AppBarLayout) parent); } else if (parent.getParent() instanceof AppBarLayout) { return ((AppBarLayout) parent.getParent()); } else { throw new IllegalStateException("Must be inside an AppBarLayout"); //TODO actually, a collapsingtoolbar } } protected void onAttachedToWindow() { super.onAttachedToWindow(); findViews(); if (!isInEditMode()) { appBarLayout.addOnOffsetChangedListener(this); } else { setExpandedValuesForEditMode(); } } private void setExpandedValuesForEditMode() { calculateValues(); updateViews(1f, 0); } private void findViews() { appBarLayout = findParentAppBarLayout(); toolbar = findSiblingToolbar(); avatarView = findAvatar(); titleView = findTitle(); } @NonNull private View findAvatar() { View avatar = this.findViewById(R.id.usericon); if (avatar == null) { throw new IllegalStateException("View with id ta_avatar not found"); } return avatar; } @NonNull private TextView findTitle() { TextView title = (TextView) this.findViewById(R.id.username); if (title == null) { throw new IllegalStateException("TextView with id ta_title not found"); } return title; } @NonNull private Toolbar findSiblingToolbar() { ViewGroup parent = ((ViewGroup) this.getParent()); for (int i = 0, c = parent.getChildCount(); i < c; i++) { View child = parent.getChildAt(i); if (child instanceof Toolbar) { return (Toolbar) child; } } throw new IllegalStateException("No toolbar found as sibling"); } @Override public void onOffsetChanged(AppBarLayout appBarLayout, int offset) { if (!valuesCalculatedAlready) { calculateValues(); valuesCalculatedAlready = true; } float expandedPercentage = 1 - (-offset / maxOffset); updateViews(expandedPercentage, offset); } private void calculateValues() { collapsedHeight = toolbar.getHeight(); expandedHeight = appBarLayout.getHeight() - toolbar.getHeight(); maxOffset = expandedHeight; } private void updateViews(float expandedPercentage, int currentOffset) { float inversePercentage = 1 - expandedPercentage; float translation = -currentOffset + ((float) toolbar.getHeight() * expandedPercentage); float currHeight = collapsedHeight + (expandedHeight - collapsedHeight) * expandedPercentage; float currentPadding = expandedPadding - (collapsedPadding - expandedPadding) * inversePercentage; float currentImageSize = collapsedImageSize + (expandedImageSize - collapsedImageSize) * expandedPercentage; float currentTextSize = collapsedTextSize + (expandedTextSize - collapsedTextSize) * expandedPercentage; setContainerOffset(translation); setContainerHeight((int) currHeight); setPadding((int) currentPadding); setAvatarSize((int) currentImageSize); setTextSize(currentTextSize); } private void setContainerOffset(float translation) { this.setTranslationY(translation); } private void setContainerHeight(int currHeight) { this.getLayoutParams().height = currHeight; } private void setPadding(int currentPadding) { this.setPadding(currentPadding+width/3, 0, 0, 0); } private void setTextSize(float currentTextSize) { titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, currentTextSize); } public void setText(String text) { titleView.setText(text); } private void setAvatarSize(int currentImageSize) { avatarView.getLayoutParams().height = currentImageSize; avatarView.getLayoutParams().width = currentImageSize; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/PagerSlidingTabStrip.java ================================================ package com.hankkin.compustrading.view; import java.util.Locale; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Typeface; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.HorizontalScrollView; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; import com.hankkin.compustrading.R; public class PagerSlidingTabStrip extends HorizontalScrollView { public interface IconTabProvider { public int getPageIconResId(int position); } // @formatter:off private static final int[] ATTRS = new int[] { android.R.attr.textSize, android.R.attr.textColor }; // @formatter:on private LinearLayout.LayoutParams defaultTabLayoutParams; private LinearLayout.LayoutParams expandedTabLayoutParams; private final PageListener pageListener = new PageListener(); public OnPageChangeListener delegatePageListener; private LinearLayout tabsContainer; private ViewPager pager; private int tabCount; private int currentPosition = 0; private int selectedPosition = 0; private float currentPositionOffset = 0f; private Paint rectPaint; private Paint dividerPaint; private int indicatorColor = 0xFF666666;// 滑动指示器颜色 private int underlineColor = 0x1A000000;//在视图的底部的全宽度的线pstsunderlinecolor颜色 private int dividerColor = 0x1A000000;//选项卡之间的分隔pstsdividercolor颜色 private boolean shouldExpand = false;//pstsshouldexpand如果设置为TRUE,每个标签都给予同样的重量,默认为false private boolean textAllCaps = true;//pststextallcaps如果为真,所有选项卡标题都是大写,默认为true private int scrollOffset = 52;//pstsscrolloffset卷轴被选择的标签的偏移 private int indicatorHeight = 8;//滑动指示器pstsindicatorheight private int underlineHeight = 2;//在视图的底部的全宽度的线pstsunderlineheight高度 private int dividerPadding = 12;//pstsdividerpadding顶部和底部填充的分频器 private int tabPadding = 24;//pststabpaddingleftright左、右填充每个选项卡 private int dividerWidth = 1;//选项卡分割线宽度 private int tabTextSize = 15;//选项卡字体大小 private int tabTextColor = 0xFF666666;//选项卡字体颜色 private int selectedTabTextColor = 0xFF666666;//当前选中字体颜色 private Typeface tabTypeface = null; private int tabTypefaceStyle = Typeface.NORMAL; private int lastScrollX = 0; private int tabBackgroundResId = R.drawable.btn_login_background;//pststabbackground背景绘制的每个标签,应该是一个statelistdrawable private Locale locale; public PagerSlidingTabStrip(Context context) { this(context, null); } public PagerSlidingTabStrip(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setFillViewport(true);//默认使子view可以拉伸来填满整个屏幕 setWillNotDraw(false);//默认不执行OnDraw()方法 //初始化盛放按钮标题的线性布局 tabsContainer = new LinearLayout(context); tabsContainer.setOrientation(LinearLayout.HORIZONTAL); tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); addView(tabsContainer); DisplayMetrics dm = getResources().getDisplayMetrics(); //导入相应资源文件 scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm); indicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm); underlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm); dividerPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm); tabPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm); dividerWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerWidth, dm); tabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm); // get system attrs (android:textSize and android:textColor) TypedArray a = context.obtainStyledAttributes(attrs, ATTRS); tabTextSize = a.getDimensionPixelSize(0, tabTextSize); tabTextColor = a.getColor(1, tabTextColor); a.recycle(); // get custom attrs a = context.obtainStyledAttributes(attrs, R.styleable.PagerSlidingTabStrip); indicatorColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsIndicatorColor, indicatorColor); //tab文字选中时的颜色,默认和滑动指示器的颜色一致 selectedTabTextColor=a.getColor(R.styleable.PagerSlidingTabStrip_selectedTabTextColor, indicatorColor); //初始化属性样式 underlineColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsUnderlineColor, underlineColor); dividerColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsDividerColor, dividerColor); indicatorHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsIndicatorHeight, indicatorHeight); underlineHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsUnderlineHeight, underlineHeight); dividerPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsDividerPadding, dividerPadding); tabPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsTabPaddingLeftRight, tabPadding); tabBackgroundResId = a.getResourceId(R.styleable.PagerSlidingTabStrip_pstsTabBackground, tabBackgroundResId); shouldExpand = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsShouldExpand, shouldExpand); scrollOffset = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsScrollOffset, scrollOffset); textAllCaps = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsTextAllCaps, textAllCaps); a.recycle(); rectPaint = new Paint(); rectPaint.setAntiAlias(true); rectPaint.setStyle(Style.FILL); dividerPaint = new Paint(); dividerPaint.setAntiAlias(true); dividerPaint.setStrokeWidth(dividerWidth); defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f); if (locale == null) { locale = getResources().getConfiguration().locale; } } public void setViewPager(ViewPager pager) { this.pager = pager; if (pager.getAdapter() == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } pager.setOnPageChangeListener(pageListener); notifyDataSetChanged(); } public void setOnPageChangeListener(OnPageChangeListener listener) { this.delegatePageListener = listener; } public void notifyDataSetChanged() { tabsContainer.removeAllViews(); tabCount = pager.getAdapter().getCount(); for (int i = 0; i < tabCount; i++) { if (pager.getAdapter() instanceof IconTabProvider) { addIconTab(i, ((IconTabProvider) pager.getAdapter()).getPageIconResId(i)); } else { addTextTab(i, pager.getAdapter().getPageTitle(i).toString()); } } updateTabStyles(); getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { getViewTreeObserver().removeGlobalOnLayoutListener(this); currentPosition = pager.getCurrentItem(); scrollToChild(currentPosition, 0); } }); } private void addTextTab(final int position, String title) { TextView tab = new TextView(getContext()); tab.setText(title); tab.setGravity(Gravity.CENTER); tab.setSingleLine(); addTab(position, tab); } private void addIconTab(final int position, int resId) { ImageButton tab = new ImageButton(getContext()); tab.setImageResource(resId); addTab(position, tab); } private void addTab(final int position, View tab) { tab.setFocusable(true); tab.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { pager.setCurrentItem(position); } }); tab.setPadding(tabPadding, 0, tabPadding, 0); tabsContainer.addView(tab, position, shouldExpand ? expandedTabLayoutParams : defaultTabLayoutParams); } private void updateTabStyles() { for (int i = 0; i < tabCount; i++) { View v = tabsContainer.getChildAt(i); v.setBackgroundResource(tabBackgroundResId); if (v instanceof TextView) { TextView tab = (TextView) v; tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize); tab.setTypeface(tabTypeface, tabTypefaceStyle); tab.setTextColor(tabTextColor); // setAllCaps() is only available from API 14, so the upper case is made manually if we are on a // pre-ICS-build if (textAllCaps) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { tab.setAllCaps(true); } else { tab.setText(tab.getText().toString().toUpperCase(locale)); } } if (i == selectedPosition) { tab.setTextColor(selectedTabTextColor); } } } } private void scrollToChild(int position, int offset) { if (tabCount == 0) { return; } int newScrollX = tabsContainer.getChildAt(position).getLeft() + offset; if (position > 0 || offset > 0) { newScrollX -= scrollOffset; } if (newScrollX != lastScrollX) { lastScrollX = newScrollX; scrollTo(newScrollX, 0); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (isInEditMode() || tabCount == 0) { return; } final int height = getHeight(); // draw underline rectPaint.setColor(underlineColor); canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint); // draw indicator line rectPaint.setColor(indicatorColor); // default: line below current tab View currentTab = tabsContainer.getChildAt(currentPosition); float lineLeft = currentTab.getLeft(); float lineRight = currentTab.getRight(); // if there is an offset, start interpolating left and right coordinates between current and next tab if (currentPositionOffset > 0f && currentPosition < tabCount - 1) { View nextTab = tabsContainer.getChildAt(currentPosition + 1); final float nextTabLeft = nextTab.getLeft(); final float nextTabRight = nextTab.getRight(); lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) * lineLeft); lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * lineRight); } canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint); // draw divider dividerPaint.setColor(dividerColor); for (int i = 0; i < tabCount - 1; i++) { View tab = tabsContainer.getChildAt(i); canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(), height - dividerPadding, dividerPaint); } } private class PageListener implements OnPageChangeListener { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { currentPosition = position; currentPositionOffset = positionOffset; scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth())); invalidate(); if (delegatePageListener != null) { delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } } @Override public void onPageScrollStateChanged(int state) { if (state == ViewPager.SCROLL_STATE_IDLE) { scrollToChild(pager.getCurrentItem(), 0); } if (delegatePageListener != null) { delegatePageListener.onPageScrollStateChanged(state); } } @Override public void onPageSelected(int position) { selectedPosition = position; updateTabStyles(); if (delegatePageListener != null) { delegatePageListener.onPageSelected(position); } } } public void setIndicatorColor(int indicatorColor) { this.indicatorColor = indicatorColor; invalidate(); } public void setIndicatorColorResource(int resId) { this.indicatorColor = getResources().getColor(resId); invalidate(); } public int getIndicatorColor() { return this.indicatorColor; } public void setIndicatorHeight(int indicatorLineHeightPx) { this.indicatorHeight = indicatorLineHeightPx; invalidate(); } public int getIndicatorHeight() { return indicatorHeight; } public void setUnderlineColor(int underlineColor) { this.underlineColor = underlineColor; invalidate(); } public void setUnderlineColorResource(int resId) { this.underlineColor = getResources().getColor(resId); invalidate(); } public int getUnderlineColor() { return underlineColor; } public void setDividerColor(int dividerColor) { this.dividerColor = dividerColor; invalidate(); } public void setDividerColorResource(int resId) { this.dividerColor = getResources().getColor(resId); invalidate(); } public int getDividerColor() { return dividerColor; } public void setUnderlineHeight(int underlineHeightPx) { this.underlineHeight = underlineHeightPx; invalidate(); } public int getUnderlineHeight() { return underlineHeight; } public void setDividerPadding(int dividerPaddingPx) { this.dividerPadding = dividerPaddingPx; invalidate(); } public int getDividerPadding() { return dividerPadding; } public void setScrollOffset(int scrollOffsetPx) { this.scrollOffset = scrollOffsetPx; invalidate(); } public int getScrollOffset() { return scrollOffset; } public void setShouldExpand(boolean shouldExpand) { this.shouldExpand = shouldExpand; notifyDataSetChanged(); } public boolean getShouldExpand() { return shouldExpand; } public boolean isTextAllCaps() { return textAllCaps; } public void setAllCaps(boolean textAllCaps) { this.textAllCaps = textAllCaps; } public void setTextSize(int textSizePx) { this.tabTextSize = textSizePx; updateTabStyles(); } public int getTextSize() { return tabTextSize; } public void setTextColor(int textColor) { this.tabTextColor = textColor; updateTabStyles(); } public void setTextColorResource(int resId) { this.tabTextColor = getResources().getColor(resId); updateTabStyles(); } public int getTextColor() { return tabTextColor; } public void setSelectedTextColor(int textColor) { this.selectedTabTextColor = textColor; updateTabStyles(); } public void setSelectedTextColorResource(int resId) { this.selectedTabTextColor = getResources().getColor(resId); updateTabStyles(); } public int getSelectedTextColor() { return selectedTabTextColor; } public void setTypeface(Typeface typeface, int style) { this.tabTypeface = typeface; this.tabTypefaceStyle = style; updateTabStyles(); } public void setTabBackground(int resId) { this.tabBackgroundResId = resId; updateTabStyles(); } public int getTabBackground() { return tabBackgroundResId; } public void setTabPaddingLeftRight(int paddingPx) { this.tabPadding = paddingPx; updateTabStyles(); } public int getTabPaddingLeftRight() { return tabPadding; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState savedState = (SavedState) state; super.onRestoreInstanceState(savedState.getSuperState()); currentPosition = savedState.currentPosition; requestLayout(); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.currentPosition = currentPosition; return savedState; } static class SavedState extends BaseSavedState { int currentPosition; public SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); currentPosition = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(currentPosition); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/RippleView.java ================================================ package com.hankkin.compustrading.view; import android.content.Context; import android.content.res.TypedArray; import android.graphics.*; import android.os.Handler; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; import android.widget.AdapterView; import android.widget.RelativeLayout; import com.hankkin.compustrading.R; /** * Created by Hankkin on 15/9/13. */ public class RippleView extends RelativeLayout { private int WIDTH; private int HEIGHT; private int frameRate = 10; private int rippleDuration = 400; private int rippleAlpha = 90; private Handler canvasHandler; private float radiusMax = 0; private boolean animationRunning = false; private int timer = 0; private int timerEmpty = 0; private int durationEmpty = -1; private float x = -1; private float y = -1; private int zoomDuration; private float zoomScale; private ScaleAnimation scaleAnimation; private Boolean hasToZoom; private Boolean isCentered; private Integer rippleType; private Paint paint; private Bitmap originBitmap; private int rippleColor; private int ripplePadding; private GestureDetector gestureDetector; private final Runnable runnable = new Runnable() { @Override public void run() { invalidate(); } }; private OnRippleCompleteListener onCompletionListener; public RippleView(Context context) { super(context); } public RippleView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public RippleView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } /** * Method that initializes all fields and sets listeners * 初始化组件并设置监听事件 * @param context Context used to create this view * @param attrs Attribute used to initialize fields */ private void init(final Context context, final AttributeSet attrs) { if (isInEditMode()) return; final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView); rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color, getResources().getColor(R.color.ripple)); rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0); hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false); isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false); rippleDuration = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, rippleDuration); frameRate = typedArray.getInteger(R.styleable.RippleView_rv_framerate, frameRate); rippleAlpha = typedArray.getInteger(R.styleable.RippleView_rv_alpha, rippleAlpha); ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0); canvasHandler = new Handler(); zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f); zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200); typedArray.recycle(); paint = new Paint(); paint.setAntiAlias(true); //设置画布抗锯齿标志 paint.setStyle(Paint.Style.FILL); //设置画图实心 paint.setColor(rippleColor); //设置画图颜色 paint.setAlpha(rippleAlpha); //设置透明度 this.setWillNotDraw(false); //设置将不绘画 /** * 创建新的手势 */ gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public void onLongPress(MotionEvent event) { super.onLongPress(event); animateRipple(event); //创建动画 sendClickEvent(true); //发送长点击事件 } @Override public boolean onSingleTapConfirmed(MotionEvent e) { return true; } @Override public boolean onSingleTapUp(MotionEvent e) { return true; } }); this.setDrawingCacheEnabled(true); //更新cache,提高绘图速度 this.setClickable(true); } @Override public void draw(Canvas canvas) { super.draw(canvas); if (animationRunning) { if (rippleDuration <= timer * frameRate) { animationRunning = false; timer = 0; durationEmpty = -1; timerEmpty = 0; canvas.save(); canvas.restore(); invalidate(); if (onCompletionListener != null) onCompletionListener.onComplete(this); return; } else canvasHandler.postDelayed(runnable, frameRate); if (timer == 0) canvas.save(); canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint); //画圆的半径 paint.setColor(Color.parseColor("#ffff4444")); //设置颜色 if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) { if (durationEmpty == -1) durationEmpty = rippleDuration - timer * frameRate; timerEmpty++; //创建圆的bitmap final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty)))); canvas.drawBitmap(tmpBitmap, 0, 0, paint); tmpBitmap.recycle(); } paint.setColor(rippleColor); if (rippleType == 1) { if ((((float) timer * frameRate) / rippleDuration) > 2f) paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty))))); else paint.setAlpha(rippleAlpha); } else paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration)))); timer++; } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); WIDTH = w; HEIGHT = h; scaleAnimation = new ScaleAnimation(2.0f, zoomScale, 2.0f, zoomScale, w / 2, h / 2); scaleAnimation.setDuration(zoomDuration); scaleAnimation.setRepeatMode(Animation.REVERSE); scaleAnimation.setRepeatCount(1); } /** * Launch Ripple animation for the current view with a MotionEvent * 推出波纹动画与位移事件的当前视图 * @param event MotionEvent registered by the Ripple gesture listener */ public void animateRipple(MotionEvent event) { createAnimation(event.getX(), event.getY()); } /** * Launch Ripple animation for the current view centered at x and y position * 以x,y为坐标启动动画 * @param x Horizontal position of the ripple center * @param y Vertical position of the ripple center */ public void animateRipple(final float x, final float y) { createAnimation(x, y); } /** * Create Ripple animation centered at x, y * * @param x Horizontal position of the ripple center * @param y Vertical position of the ripple center */ private void createAnimation(final float x, final float y) { if (this.isEnabled() && !animationRunning) { if (hasToZoom) this.startAnimation(scaleAnimation); radiusMax = Math.max(WIDTH, HEIGHT); if (rippleType != 2) radiusMax /= 1; radiusMax -= ripplePadding; if (isCentered || rippleType == 1) { this.x = getMeasuredWidth() ; this.y = getMeasuredHeight() ; } else { this.x = x; this.y = y; } animationRunning = true; if (rippleType == 1 && originBitmap == null) originBitmap = getDrawingCache(true); invalidate(); } } @Override public boolean onTouchEvent(MotionEvent event) { if (gestureDetector.onTouchEvent(event)) { animateRipple(event); sendClickEvent(false); } return super.onTouchEvent(event); } @Override public boolean onInterceptTouchEvent(MotionEvent event) { this.onTouchEvent(event); return super.onInterceptTouchEvent(event); } /** * Send a click event if parent view is a Listview instance * 若为Listview发送点击事件 * @param isLongClick Is the event a long click ? */ private void sendClickEvent(final Boolean isLongClick) { if (getParent() instanceof AdapterView) { final AdapterView adapterView = (AdapterView) getParent(); final int position = adapterView.getPositionForView(this); final long id = adapterView.getItemIdAtPosition(position); if (isLongClick) { if (adapterView.getOnItemLongClickListener() != null) adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id); } else { if (adapterView.getOnItemClickListener() != null) adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id); } } } /** * 获取波纹圆形图片 * @param radius * @return */ private Bitmap getCircleBitmap(final int radius) { final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(), Bitmap.Config.ARGB_8888); final Canvas canvas = new Canvas(output); final Paint paint = new Paint(); final Rect rect = new Rect((int)(x - radius), (int)(y - radius), (int)(x + radius), (int)(y + radius)); paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); canvas.drawCircle(x, y, radius, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(originBitmap, rect, rect, paint); return output; } /** * Set Ripple color, default is #FFFFFF * 设置动画的颜色,默认为白色 * @param rippleColor New color resource */ public void setRippleColor(int rippleColor) { this.rippleColor = getResources().getColor(rippleColor); } public int getRippleColor() { return rippleColor; } public RippleType getRippleType() { return RippleType.values()[rippleType]; } /** * Set Ripple type, default is RippleType.SIMPLE * * @param rippleType New Ripple type for next animation */ public void setRippleType(final RippleType rippleType) { this.rippleType = rippleType.ordinal(); } public Boolean isCentered() { return isCentered; } /** * Set if ripple animation has to be centered in its parent view or not, default is False * * @param isCentered */ public void setCentered(final Boolean isCentered) { this.isCentered = isCentered; } public int getRipplePadding() { return ripplePadding; } /** * Set Ripple padding if you want to avoid some graphic glitch * * @param ripplePadding New Ripple padding in pixel, default is 0px */ public void setRipplePadding(int ripplePadding) { this.ripplePadding = ripplePadding; } public Boolean isZooming() { return hasToZoom; } /** * At the end of Ripple effect, the child views has to zoom * * @param hasToZoom Do the child views have to zoom ? default is False */ public void setZooming(Boolean hasToZoom) { this.hasToZoom = hasToZoom; } public float getZoomScale() { return zoomScale; } /** * Scale of the end animation * * @param zoomScale Value of scale animation, default is 1.03f */ public void setZoomScale(float zoomScale) { this.zoomScale = zoomScale; } public int getZoomDuration() { return zoomDuration; } /** * Duration of the ending animation in ms * * @param zoomDuration Duration, default is 200ms */ public void setZoomDuration(int zoomDuration) { this.zoomDuration = zoomDuration; } public int getRippleDuration() { return rippleDuration; } /** * Duration of the Ripple animation in ms * * @param rippleDuration Duration, default is 400ms */ public void setRippleDuration(int rippleDuration) { this.rippleDuration = rippleDuration; } public int getFrameRate() { return frameRate; } /** * Set framerate for Ripple animation * * @param frameRate New framerate value, default is 10 */ public void setFrameRate(int frameRate) { this.frameRate = frameRate; } public int getRippleAlpha() { return rippleAlpha; } /** * Set alpha for ripple effect color * * @param rippleAlpha Alpha value between 0 and 255, default is 90 */ public void setRippleAlpha(int rippleAlpha) { this.rippleAlpha = rippleAlpha; } public void setOnRippleCompleteListener(OnRippleCompleteListener listener) { this.onCompletionListener = listener; } /** * Defines a callback called at the end of the Ripple effect */ public interface OnRippleCompleteListener { void onComplete(RippleView rippleView); } public enum RippleType { SIMPLE(0), DOUBLE(1), RECTANGLE(2); int type; RippleType(int type) { this.type = type; } } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/RoundedImageView.java ================================================ /** * 圆形控件,显示头像 * by黄海杰 at:2015-4-7 */ package com.hankkin.compustrading.view; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.NinePatchDrawable; import android.util.AttributeSet; import android.widget.ImageView; import com.hankkin.compustrading.R; public class RoundedImageView extends ImageView { public RoundedImageView(Context context) { super(context); } public RoundedImageView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public RoundedImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } private boolean hasBound = false; private int boundColor = -1; private void init(Context context, AttributeSet attrs) { if (attrs != null) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView); hasBound = a.getBoolean( R.styleable.RoundImageView_roundImageView_hasRound, false); boundColor = a.getColor( R.styleable.RoundImageView_roundImageView_roundColor, context.getResources().getColor(R.color.white)); } } @Override protected void onDraw(Canvas canvas) { Bitmap b = null; if (getDrawable() instanceof NinePatchDrawable) { NinePatchDrawable dr = (NinePatchDrawable) getDrawable(); b = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888); Drawable drawable = getResources().getDrawable( R.drawable.defaut); Canvas canvas1 = new Canvas(b); drawable.setBounds(0, 0, canvas1.getWidth(), canvas1.getHeight()); // drawable.draw(canvas); dr.draw(canvas1); } else { Drawable drawable = getDrawable(); if (drawable == null) { return; } if (getWidth() == 0 || getHeight() == 0) { return; } b = ((BitmapDrawable) drawable).getBitmap(); } if (b == null) { NinePatchDrawable drawable = (NinePatchDrawable) getResources() .getDrawable(R.drawable.defaut); b = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888); Canvas canvas1 = new Canvas(b); drawable.setBounds(0, 0, canvas1.getWidth(), canvas1.getHeight()); drawable.draw(canvas1); } Bitmap bitmap = b.copy(Config.ARGB_8888, true); int w = getWidth(), h = getHeight(); Bitmap roundBitmap = getCroppedBitmap(bitmap, w); canvas.drawBitmap(roundBitmap, 0, 0, null); } public Bitmap getCroppedBitmap(Bitmap bmp, int radius) { Bitmap sbmp; if (bmp.getWidth() != radius || bmp.getHeight() != radius) sbmp = Bitmap.createScaledBitmap(bmp, radius, radius, false); else sbmp = bmp; Bitmap output = Bitmap.createBitmap(sbmp.getWidth(), sbmp.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xffa19774; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, sbmp.getWidth(), sbmp.getHeight()); paint.setAntiAlias(true); paint.setFilterBitmap(true); paint.setDither(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(Color.parseColor("#BAB399")); canvas.drawCircle(sbmp.getWidth() / 2 + 0.7f, sbmp.getHeight() / 2 + 0.7f, sbmp.getWidth() / 2 + 0.1f - 3, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(sbmp, rect, rect, paint); // draw border if (hasBound) { Paint paint2 = new Paint(); if (boundColor != -1) { paint2.setColor(boundColor); } else { paint2.setColor(Color.parseColor("#ffffff")); } paint2.setStyle(Paint.Style.STROKE); paint2.setStrokeWidth((float) pxFromDp(this.getContext(), 1)); paint2.setAntiAlias(true); canvas.drawCircle(sbmp.getWidth() / 2 + 0.7f, sbmp.getHeight() / 2 + 0.7f, sbmp.getWidth() / 2 + 0.1f - 3 + 1 - pxFromDp(this.getContext(), 1), paint2); } return output; } public static float pxFromDp(Context context, float dp) { return dp * context.getResources().getDisplayMetrics().density; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/floatbutton/AbsListViewScrollDetector.java ================================================ package com.hankkin.compustrading.view.floatbutton; import android.support.annotation.NonNull; import android.view.View; import android.widget.AbsListView; abstract class AbsListViewScrollDetector implements AbsListView.OnScrollListener { private int mLastScrollY; private int mPreviousFirstVisibleItem; private AbsListView mListView; private int mScrollThreshold; abstract void onScrollUp(); abstract void onScrollDown(); @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if(totalItemCount == 0) return; if (isSameRow(firstVisibleItem)) { int newScrollY = getTopItemScrollY(); boolean isSignificantDelta = Math.abs(mLastScrollY - newScrollY) > mScrollThreshold; if (isSignificantDelta) { if (mLastScrollY > newScrollY) { onScrollUp(); } else { onScrollDown(); } } mLastScrollY = newScrollY; } else { if (firstVisibleItem > mPreviousFirstVisibleItem) { onScrollUp(); } else { onScrollDown(); } mLastScrollY = getTopItemScrollY(); mPreviousFirstVisibleItem = firstVisibleItem; } } public void setScrollThreshold(int scrollThreshold) { mScrollThreshold = scrollThreshold; } public void setListView(@NonNull AbsListView listView) { mListView = listView; } private boolean isSameRow(int firstVisibleItem) { return firstVisibleItem == mPreviousFirstVisibleItem; } private int getTopItemScrollY() { if (mListView == null || mListView.getChildAt(0) == null) return 0; View topChild = mListView.getChildAt(0); return topChild.getTop(); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/floatbutton/AddFloatingActionButton.java ================================================ package com.hankkin.compustrading.view.floatbutton; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.Shape; import android.support.annotation.ColorRes; import android.support.annotation.DrawableRes; import android.util.AttributeSet; import com.hankkin.compustrading.R; public class AddFloatingActionButton extends FloatingActionButton { int mPlusColor; public AddFloatingActionButton(Context context) { this(context, null); } public AddFloatingActionButton(Context context, AttributeSet attrs) { super(context, attrs); } public AddFloatingActionButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override void init(Context context, AttributeSet attributeSet) { TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.AddFloatingActionButton, 0, 0); mPlusColor = attr.getColor(R.styleable.AddFloatingActionButton_fab_plusIconColor, getColor(android.R.color.white)); attr.recycle(); super.init(context, attributeSet); } /** * @return the current Color of plus icon. */ public int getPlusColor() { return mPlusColor; } public void setPlusColorResId(@ColorRes int plusColor) { setPlusColor(getColor(plusColor)); } public void setPlusColor(int color) { if (mPlusColor != color) { mPlusColor = color; updateBackground(); } } @Override public void setIcon(@DrawableRes int icon) { throw new UnsupportedOperationException("Use FloatingActionButton if you want to use custom icon"); } @Override Drawable getIconDrawable() { final float iconSize = getDimension(R.dimen.fab_icon_size); final float iconHalfSize = iconSize / 2f; final float plusSize = getDimension(R.dimen.fab_plus_icon_size); final float plusHalfStroke = getDimension(R.dimen.fab_plus_icon_stroke) / 2f; final float plusOffset = (iconSize - plusSize) / 2f; final Shape shape = new Shape() { @Override public void draw(Canvas canvas, Paint paint) { canvas.drawRect(plusOffset, iconHalfSize - plusHalfStroke, iconSize - plusOffset, iconHalfSize + plusHalfStroke, paint); canvas.drawRect(iconHalfSize - plusHalfStroke, plusOffset, iconHalfSize + plusHalfStroke, iconSize - plusOffset, paint); } }; ShapeDrawable drawable = new ShapeDrawable(shape); final Paint paint = drawable.getPaint(); paint.setColor(mPlusColor); paint.setStyle(Style.FILL); paint.setAntiAlias(true); return drawable; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/floatbutton/FloatingActionButton.java ================================================ package com.hankkin.compustrading.view.floatbutton; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.Shader.TileMode; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.ShapeDrawable.ShaderFactory; import android.graphics.drawable.StateListDrawable; import android.graphics.drawable.shapes.OvalShape; import android.os.Build; import android.os.Build.VERSION_CODES; import android.support.annotation.ColorRes; import android.support.annotation.DimenRes; import android.support.annotation.DrawableRes; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.widget.ImageButton; import android.widget.TextView; import com.hankkin.compustrading.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public class FloatingActionButton extends ImageButton { public static final int SIZE_NORMAL = 0; public static final int SIZE_MINI = 1; @Retention(RetentionPolicy.SOURCE) @IntDef({ SIZE_NORMAL, SIZE_MINI }) public @interface FAB_SIZE { } int mColorNormal; int mColorPressed; int mColorDisabled; String mTitle; @DrawableRes private int mIcon; private Drawable mIconDrawable; private int mSize; private float mCircleSize; private float mShadowRadius; private float mShadowOffset; private int mDrawableSize; boolean mStrokeVisible; public FloatingActionButton(Context context) { this(context, null); } public FloatingActionButton(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public FloatingActionButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } void init(Context context, AttributeSet attributeSet) { TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionButton, 0, 0); mColorNormal = attr.getColor(R.styleable.FloatingActionButton_fab_colorNormal, getColor(android.R.color.holo_blue_dark)); mColorPressed = attr.getColor(R.styleable.FloatingActionButton_fab_colorPressed, getColor(android.R.color.holo_blue_light)); mColorDisabled = attr.getColor(R.styleable.FloatingActionButton_fab_colorDisabled, getColor(android.R.color.darker_gray)); mSize = attr.getInt(R.styleable.FloatingActionButton_fab_size, SIZE_NORMAL); mIcon = attr.getResourceId(R.styleable.FloatingActionButton_fab_icon, 0); mTitle = attr.getString(R.styleable.FloatingActionButton_fab_title); mStrokeVisible = attr.getBoolean(R.styleable.FloatingActionButton_fab_stroke_visible, true); attr.recycle(); updateCircleSize(); mShadowRadius = getDimension(R.dimen.fab_shadow_radius); mShadowOffset = getDimension(R.dimen.fab_shadow_offset); updateDrawableSize(); updateBackground(); } private void updateDrawableSize() { mDrawableSize = (int) (mCircleSize + 2 * mShadowRadius); } private void updateCircleSize() { mCircleSize = getDimension(mSize == SIZE_NORMAL ? R.dimen.fab_size_normal : R.dimen.fab_size_mini); } public void setSize(@FAB_SIZE int size) { if (size != SIZE_MINI && size != SIZE_NORMAL) { throw new IllegalArgumentException("Use @FAB_SIZE constants only!"); } if (mSize != size) { mSize = size; updateCircleSize(); updateDrawableSize(); updateBackground(); } } @FAB_SIZE public int getSize() { return mSize; } public void setIcon(@DrawableRes int icon) { if (mIcon != icon) { mIcon = icon; mIconDrawable = null; updateBackground(); } } public void setIconDrawable(@NonNull Drawable iconDrawable) { if (mIconDrawable != iconDrawable) { mIcon = 0; mIconDrawable = iconDrawable; updateBackground(); } } /** * @return the current Color for normal state. */ public int getColorNormal() { return mColorNormal; } public void setColorNormalResId(@ColorRes int colorNormal) { setColorNormal(getColor(colorNormal)); } public void setColorNormal(int color) { if (mColorNormal != color) { mColorNormal = color; updateBackground(); } } /** * @return the current color for pressed state. */ public int getColorPressed() { return mColorPressed; } public void setColorPressedResId(@ColorRes int colorPressed) { setColorPressed(getColor(colorPressed)); } public void setColorPressed(int color) { if (mColorPressed != color) { mColorPressed = color; updateBackground(); } } /** * @return the current color for disabled state. */ public int getColorDisabled() { return mColorDisabled; } public void setColorDisabledResId(@ColorRes int colorDisabled) { setColorDisabled(getColor(colorDisabled)); } public void setColorDisabled(int color) { if (mColorDisabled != color) { mColorDisabled = color; updateBackground(); } } public void setStrokeVisible(boolean visible) { if (mStrokeVisible != visible) { mStrokeVisible = visible; updateBackground(); } } public boolean isStrokeVisible() { return mStrokeVisible; } int getColor(@ColorRes int id) { return getResources().getColor(id); } float getDimension(@DimenRes int id) { return getResources().getDimension(id); } public void setTitle(String title) { mTitle = title; TextView label = getLabelView(); if (label != null) { label.setText(title); } } TextView getLabelView() { return (TextView) getTag(R.id.fab_label); } public String getTitle() { return mTitle; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(mDrawableSize, mDrawableSize); } void updateBackground() { final float strokeWidth = getDimension(R.dimen.fab_stroke_width); final float halfStrokeWidth = strokeWidth / 2f; LayerDrawable layerDrawable = new LayerDrawable( new Drawable[] { getResources().getDrawable(mSize == SIZE_NORMAL ? R.drawable.fab_bg_normal : R.drawable.fab_bg_mini), createFillDrawable(strokeWidth), createOuterStrokeDrawable(strokeWidth), getIconDrawable() }); int iconOffset = (int) (mCircleSize - getDimension(R.dimen.fab_icon_size)) / 2; int circleInsetHorizontal = (int) (mShadowRadius); int circleInsetTop = (int) (mShadowRadius - mShadowOffset); int circleInsetBottom = (int) (mShadowRadius + mShadowOffset); layerDrawable.setLayerInset(1, circleInsetHorizontal, circleInsetTop, circleInsetHorizontal, circleInsetBottom); layerDrawable.setLayerInset(2, (int) (circleInsetHorizontal - halfStrokeWidth), (int) (circleInsetTop - halfStrokeWidth), (int) (circleInsetHorizontal - halfStrokeWidth), (int) (circleInsetBottom - halfStrokeWidth)); layerDrawable.setLayerInset(3, circleInsetHorizontal + iconOffset, circleInsetTop + iconOffset, circleInsetHorizontal + iconOffset, circleInsetBottom + iconOffset); setBackgroundCompat(layerDrawable); } Drawable getIconDrawable() { if (mIconDrawable != null) { return mIconDrawable; } else if (mIcon != 0) { return getResources().getDrawable(mIcon); } else { return new ColorDrawable(Color.TRANSPARENT); } } private StateListDrawable createFillDrawable(float strokeWidth) { StateListDrawable drawable = new StateListDrawable(); drawable.addState(new int[] { -android.R.attr.state_enabled }, createCircleDrawable(mColorDisabled, strokeWidth)); drawable.addState(new int[] { android.R.attr.state_pressed }, createCircleDrawable(mColorPressed, strokeWidth)); drawable.addState(new int[] { }, createCircleDrawable(mColorNormal, strokeWidth)); return drawable; } private Drawable createCircleDrawable(int color, float strokeWidth) { int alpha = Color.alpha(color); int opaqueColor = opaque(color); ShapeDrawable fillDrawable = new ShapeDrawable(new OvalShape()); final Paint paint = fillDrawable.getPaint(); paint.setAntiAlias(true); paint.setColor(opaqueColor); Drawable[] layers = { fillDrawable, createInnerStrokesDrawable(opaqueColor, strokeWidth) }; LayerDrawable drawable = alpha == 255 || !mStrokeVisible ? new LayerDrawable(layers) : new TranslucentLayerDrawable(alpha, layers); int halfStrokeWidth = (int) (strokeWidth / 2f); drawable.setLayerInset(1, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth); return drawable; } private static class TranslucentLayerDrawable extends LayerDrawable { private final int mAlpha; public TranslucentLayerDrawable(int alpha, Drawable... layers) { super(layers); mAlpha = alpha; } @Override public void draw(Canvas canvas) { Rect bounds = getBounds(); canvas.saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom, mAlpha, Canvas.ALL_SAVE_FLAG); super.draw(canvas); canvas.restore(); } } private Drawable createOuterStrokeDrawable(float strokeWidth) { ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape()); final Paint paint = shapeDrawable.getPaint(); paint.setAntiAlias(true); paint.setStrokeWidth(strokeWidth); paint.setStyle(Style.STROKE); paint.setColor(Color.BLACK); paint.setAlpha(opacityToAlpha(0.02f)); return shapeDrawable; } private int opacityToAlpha(float opacity) { return (int) (255f * opacity); } private int darkenColor(int argb) { return adjustColorBrightness(argb, 0.9f); } private int lightenColor(int argb) { return adjustColorBrightness(argb, 1.1f); } private int adjustColorBrightness(int argb, float factor) { float[] hsv = new float[3]; Color.colorToHSV(argb, hsv); hsv[2] = Math.min(hsv[2] * factor, 1f); return Color.HSVToColor(Color.alpha(argb), hsv); } private int halfTransparent(int argb) { return Color.argb( Color.alpha(argb) / 2, Color.red(argb), Color.green(argb), Color.blue(argb) ); } private int opaque(int argb) { return Color.rgb( Color.red(argb), Color.green(argb), Color.blue(argb) ); } private Drawable createInnerStrokesDrawable(final int color, float strokeWidth) { if (!mStrokeVisible) { return new ColorDrawable(Color.TRANSPARENT); } ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape()); final int bottomStrokeColor = darkenColor(color); final int bottomStrokeColorHalfTransparent = halfTransparent(bottomStrokeColor); final int topStrokeColor = lightenColor(color); final int topStrokeColorHalfTransparent = halfTransparent(topStrokeColor); final Paint paint = shapeDrawable.getPaint(); paint.setAntiAlias(true); paint.setStrokeWidth(strokeWidth); paint.setStyle(Style.STROKE); shapeDrawable.setShaderFactory(new ShaderFactory() { @Override public Shader resize(int width, int height) { return new LinearGradient(width / 2, 0, width / 2, height, new int[] { topStrokeColor, topStrokeColorHalfTransparent, color, bottomStrokeColorHalfTransparent, bottomStrokeColor }, new float[] { 0f, 0.2f, 0.5f, 0.8f, 1f }, TileMode.CLAMP ); } }); return shapeDrawable; } @SuppressWarnings("deprecation") @SuppressLint("NewApi") private void setBackgroundCompat(Drawable drawable) { if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { setBackground(drawable); } else { setBackgroundDrawable(drawable); } } @Override public void setVisibility(int visibility) { TextView label = getLabelView(); if (label != null) { label.setVisibility(visibility); } super.setVisibility(visibility); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/floatbutton/FloatingActionsMenu.java ================================================ package com.hankkin.compustrading.view.floatbutton; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.ColorRes; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.view.ContextThemeWrapper; import android.view.TouchDelegate; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; import android.widget.AbsListView; import android.widget.TextView; import com.hankkin.compustrading.R; import com.hankkin.compustrading.ScrollDirectionListener; import com.nineoldandroids.view.ViewHelper; import com.nineoldandroids.view.ViewPropertyAnimator; public class FloatingActionsMenu extends ViewGroup { public static final int EXPAND_UP = 0; public static final int EXPAND_DOWN = 1; public static final int EXPAND_LEFT = 2; public static final int EXPAND_RIGHT = 3; public static final int LABELS_ON_LEFT_SIDE = 0; public static final int LABELS_ON_RIGHT_SIDE = 1; private static final int ANIMATION_DURATION = 300; private static final float COLLAPSED_PLUS_ROTATION = 0f; private static final float EXPANDED_PLUS_ROTATION = 90f + 45f; private int mAddButtonPlusColor; private int mAddButtonColorNormal; private int mAddButtonColorPressed; private int mAddButtonSize; private boolean mAddButtonStrokeVisible; private int mExpandDirection; private int mButtonSpacing; private int mLabelsMargin; private int mLabelsVerticalOffset; private boolean mExpanded; private AnimatorSet mExpandAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION); private AnimatorSet mCollapseAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION); private AddFloatingActionButton mAddButton; private RotatingDrawable mRotatingDrawable; private int mMaxButtonWidth; private int mMaxButtonHeight; private int mLabelsStyle; private int mLabelsPosition; private int mButtonsCount; private boolean mVisible; private int mScrollThreshold; private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator(); private static final int TRANSLATE_DURATION_MILLIS = 200; private TouchDelegateGroup mTouchDelegateGroup; private OnFloatingActionsMenuUpdateListener mListener; public interface OnFloatingActionsMenuUpdateListener { void onMenuExpanded(); void onMenuCollapsed(); } public FloatingActionsMenu(Context context) { this(context, null); } public FloatingActionsMenu(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public FloatingActionsMenu(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } private void init(Context context, AttributeSet attributeSet) { mButtonSpacing = (int) (getResources().getDimension(R.dimen.fab_actions_spacing) - getResources().getDimension(R.dimen.fab_shadow_radius) - getResources().getDimension(R.dimen.fab_shadow_offset)); mLabelsMargin = getResources().getDimensionPixelSize(R.dimen.fab_labels_margin); mLabelsVerticalOffset = getResources().getDimensionPixelSize(R.dimen.fab_shadow_offset); mTouchDelegateGroup = new TouchDelegateGroup(this); setTouchDelegate(mTouchDelegateGroup); TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionsMenu, 0, 0); mAddButtonPlusColor = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonPlusIconColor, getColor(android.R.color.white)); mAddButtonColorNormal = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorNormal, getColor(android.R.color.holo_blue_dark)); mAddButtonColorPressed = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorPressed, getColor(android.R.color.holo_blue_light)); mAddButtonSize = attr.getInt(R.styleable.FloatingActionsMenu_fab_addButtonSize, FloatingActionButton.SIZE_NORMAL); mAddButtonStrokeVisible = attr.getBoolean(R.styleable.FloatingActionsMenu_fab_addButtonStrokeVisible, true); mExpandDirection = attr.getInt(R.styleable.FloatingActionsMenu_fab_expandDirection, EXPAND_UP); mLabelsStyle = attr.getResourceId(R.styleable.FloatingActionsMenu_fab_labelStyle, 0); mLabelsPosition = attr.getInt(R.styleable.FloatingActionsMenu_fab_labelsPosition, LABELS_ON_LEFT_SIDE); attr.recycle(); mScrollThreshold = getResources().getDimensionPixelOffset(R.dimen.tiny_space); if (mLabelsStyle != 0 && expandsHorizontally()) { throw new IllegalStateException("Action labels in horizontal expand orientation is not supported."); } createAddButton(context); } public void setOnFloatingActionsMenuUpdateListener(OnFloatingActionsMenuUpdateListener listener) { mListener = listener; } private boolean expandsHorizontally() { return mExpandDirection == EXPAND_LEFT || mExpandDirection == EXPAND_RIGHT; } private static class RotatingDrawable extends LayerDrawable { public RotatingDrawable(Drawable drawable) { super(new Drawable[] { drawable }); } private float mRotation; @SuppressWarnings("UnusedDeclaration") public float getRotation() { return mRotation; } @SuppressWarnings("UnusedDeclaration") public void setRotation(float rotation) { mRotation = rotation; invalidateSelf(); } @Override public void draw(Canvas canvas) { canvas.save(); canvas.rotate(mRotation, getBounds().centerX(), getBounds().centerY()); super.draw(canvas); canvas.restore(); } } private void createAddButton(Context context) { mAddButton = new AddFloatingActionButton(context) { @Override void updateBackground() { mPlusColor = mAddButtonPlusColor; mColorNormal = mAddButtonColorNormal; mColorPressed = mAddButtonColorPressed; mStrokeVisible = mAddButtonStrokeVisible; super.updateBackground(); } @Override Drawable getIconDrawable() { final RotatingDrawable rotatingDrawable = new RotatingDrawable(super.getIconDrawable()); mRotatingDrawable = rotatingDrawable; final OvershootInterpolator interpolator = new OvershootInterpolator(); final ObjectAnimator collapseAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", EXPANDED_PLUS_ROTATION, COLLAPSED_PLUS_ROTATION); final ObjectAnimator expandAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", COLLAPSED_PLUS_ROTATION, EXPANDED_PLUS_ROTATION); collapseAnimator.setInterpolator(interpolator); expandAnimator.setInterpolator(interpolator); mExpandAnimation.play(expandAnimator); mCollapseAnimation.play(collapseAnimator); return rotatingDrawable; } }; mAddButton.setId(R.id.fab_expand_menu_button); mAddButton.setSize(mAddButtonSize); mAddButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { toggle(); } }); addView(mAddButton, super.generateDefaultLayoutParams()); mButtonsCount++; } public void addButton(FloatingActionButton button) { addView(button, mButtonsCount - 1); mButtonsCount++; if (mLabelsStyle != 0) { createLabels(); } } public void removeButton(FloatingActionButton button) { removeView(button.getLabelView()); removeView(button); button.setTag(R.id.fab_label, null); mButtonsCount--; } private int getColor(@ColorRes int id) { return getResources().getColor(id); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildren(widthMeasureSpec, heightMeasureSpec); int width = 0; int height = 0; mMaxButtonWidth = 0; mMaxButtonHeight = 0; int maxLabelWidth = 0; for (int i = 0; i < mButtonsCount; i++) { View child = getChildAt(i); if (child.getVisibility() == GONE) { continue; } switch (mExpandDirection) { case EXPAND_UP: case EXPAND_DOWN: mMaxButtonWidth = Math.max(mMaxButtonWidth, child.getMeasuredWidth()); height += child.getMeasuredHeight(); break; case EXPAND_LEFT: case EXPAND_RIGHT: width += child.getMeasuredWidth(); mMaxButtonHeight = Math.max(mMaxButtonHeight, child.getMeasuredHeight()); break; } if (!expandsHorizontally()) { TextView label = (TextView) child.getTag(R.id.fab_label); if (label != null) { maxLabelWidth = Math.max(maxLabelWidth, label.getMeasuredWidth()); } } } if (!expandsHorizontally()) { width = mMaxButtonWidth + (maxLabelWidth > 0 ? maxLabelWidth + mLabelsMargin : 0); } else { height = mMaxButtonHeight; } switch (mExpandDirection) { case EXPAND_UP: case EXPAND_DOWN: height += mButtonSpacing * (mButtonsCount - 1); height = adjustForOvershoot(height); break; case EXPAND_LEFT: case EXPAND_RIGHT: width += mButtonSpacing * (mButtonsCount - 1); width = adjustForOvershoot(width); break; } setMeasuredDimension(width, height); } private int adjustForOvershoot(int dimension) { return dimension * 12 / 10; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { switch (mExpandDirection) { case EXPAND_UP: case EXPAND_DOWN: boolean expandUp = mExpandDirection == EXPAND_UP; if (changed) { mTouchDelegateGroup.clearTouchDelegates(); } int addButtonY = expandUp ? b - t - mAddButton.getMeasuredHeight() : 0; // Ensure mAddButton is centered on the line where the buttons should be int buttonsHorizontalCenter = mLabelsPosition == LABELS_ON_LEFT_SIDE ? r - l - mMaxButtonWidth / 2 : mMaxButtonWidth / 2; int addButtonLeft = buttonsHorizontalCenter - mAddButton.getMeasuredWidth() / 2; mAddButton.layout(addButtonLeft, addButtonY, addButtonLeft + mAddButton.getMeasuredWidth(), addButtonY + mAddButton.getMeasuredHeight()); int labelsOffset = mMaxButtonWidth / 2 + mLabelsMargin; int labelsXNearButton = mLabelsPosition == LABELS_ON_LEFT_SIDE ? buttonsHorizontalCenter - labelsOffset : buttonsHorizontalCenter + labelsOffset; int nextY = expandUp ? addButtonY - mButtonSpacing : addButtonY + mAddButton.getMeasuredHeight() + mButtonSpacing; for (int i = mButtonsCount - 1; i >= 0; i--) { final View child = getChildAt(i); if (child == mAddButton || child.getVisibility() == GONE) continue; int childX = buttonsHorizontalCenter - child.getMeasuredWidth() / 2; int childY = expandUp ? nextY - child.getMeasuredHeight() : nextY; child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight()); float collapsedTranslation = addButtonY - childY; float expandedTranslation = 0f; child.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation); child.setAlpha(mExpanded ? 1f : 0f); LayoutParams params = (LayoutParams) child.getLayoutParams(); params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation); params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation); params.setAnimationsTarget(child); View label = (View) child.getTag(R.id.fab_label); if (label != null) { int labelXAwayFromButton = mLabelsPosition == LABELS_ON_LEFT_SIDE ? labelsXNearButton - label.getMeasuredWidth() : labelsXNearButton + label.getMeasuredWidth(); int labelLeft = mLabelsPosition == LABELS_ON_LEFT_SIDE ? labelXAwayFromButton : labelsXNearButton; int labelRight = mLabelsPosition == LABELS_ON_LEFT_SIDE ? labelsXNearButton : labelXAwayFromButton; int labelTop = childY - mLabelsVerticalOffset + (child.getMeasuredHeight() - label.getMeasuredHeight()) / 2; label.layout(labelLeft, labelTop, labelRight, labelTop + label.getMeasuredHeight()); Rect touchArea = new Rect( Math.min(childX, labelLeft), childY - mButtonSpacing / 2, Math.max(childX + child.getMeasuredWidth(), labelRight), childY + child.getMeasuredHeight() + mButtonSpacing / 2); mTouchDelegateGroup.addTouchDelegate(new TouchDelegate(touchArea, child)); label.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation); label.setAlpha(mExpanded ? 1f : 0f); LayoutParams labelParams = (LayoutParams) label.getLayoutParams(); labelParams.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation); labelParams.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation); labelParams.setAnimationsTarget(label); } nextY = expandUp ? childY - mButtonSpacing : childY + child.getMeasuredHeight() + mButtonSpacing; } break; case EXPAND_LEFT: case EXPAND_RIGHT: boolean expandLeft = mExpandDirection == EXPAND_LEFT; int addButtonX = expandLeft ? r - l - mAddButton.getMeasuredWidth() : 0; // Ensure mAddButton is centered on the line where the buttons should be int addButtonTop = b - t - mMaxButtonHeight + (mMaxButtonHeight - mAddButton.getMeasuredHeight()) / 2; mAddButton.layout(addButtonX, addButtonTop, addButtonX + mAddButton.getMeasuredWidth(), addButtonTop + mAddButton.getMeasuredHeight()); int nextX = expandLeft ? addButtonX - mButtonSpacing : addButtonX + mAddButton.getMeasuredWidth() + mButtonSpacing; for (int i = mButtonsCount - 1; i >= 0; i--) { final View child = getChildAt(i); if (child == mAddButton || child.getVisibility() == GONE) continue; int childX = expandLeft ? nextX - child.getMeasuredWidth() : nextX; int childY = addButtonTop + (mAddButton.getMeasuredHeight() - child.getMeasuredHeight()) / 2; child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight()); float collapsedTranslation = addButtonX - childX; float expandedTranslation = 0f; child.setTranslationX(mExpanded ? expandedTranslation : collapsedTranslation); child.setAlpha(mExpanded ? 1f : 0f); LayoutParams params = (LayoutParams) child.getLayoutParams(); params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation); params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation); params.setAnimationsTarget(child); nextX = expandLeft ? childX - mButtonSpacing : childX + child.getMeasuredWidth() + mButtonSpacing; } break; } } @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new LayoutParams(super.generateDefaultLayoutParams()); } @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(super.generateLayoutParams(attrs)); } @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new LayoutParams(super.generateLayoutParams(p)); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return super.checkLayoutParams(p); } private static Interpolator sExpandInterpolator = new OvershootInterpolator(); private static Interpolator sCollapseInterpolator = new DecelerateInterpolator(3f); private static Interpolator sAlphaExpandInterpolator = new DecelerateInterpolator(); private class LayoutParams extends ViewGroup.LayoutParams { private ObjectAnimator mExpandDir = new ObjectAnimator(); private ObjectAnimator mExpandAlpha = new ObjectAnimator(); private ObjectAnimator mCollapseDir = new ObjectAnimator(); private ObjectAnimator mCollapseAlpha = new ObjectAnimator(); private boolean animationsSetToPlay; public LayoutParams(ViewGroup.LayoutParams source) { super(source); mExpandDir.setInterpolator(sExpandInterpolator); mExpandAlpha.setInterpolator(sAlphaExpandInterpolator); mCollapseDir.setInterpolator(sCollapseInterpolator); mCollapseAlpha.setInterpolator(sCollapseInterpolator); mCollapseAlpha.setProperty(View.ALPHA); mCollapseAlpha.setFloatValues(1f, 0f); mExpandAlpha.setProperty(View.ALPHA); mExpandAlpha.setFloatValues(0f, 1f); switch (mExpandDirection) { case EXPAND_UP: case EXPAND_DOWN: mCollapseDir.setProperty(View.TRANSLATION_Y); mExpandDir.setProperty(View.TRANSLATION_Y); break; case EXPAND_LEFT: case EXPAND_RIGHT: mCollapseDir.setProperty(View.TRANSLATION_X); mExpandDir.setProperty(View.TRANSLATION_X); break; } } public void setAnimationsTarget(View view) { mCollapseAlpha.setTarget(view); mCollapseDir.setTarget(view); mExpandAlpha.setTarget(view); mExpandDir.setTarget(view); // Now that the animations have targets, set them to be played if (!animationsSetToPlay) { addLayerTypeListener(mExpandDir, view); addLayerTypeListener(mCollapseDir, view); mCollapseAnimation.play(mCollapseAlpha); mCollapseAnimation.play(mCollapseDir); mExpandAnimation.play(mExpandAlpha); mExpandAnimation.play(mExpandDir); animationsSetToPlay = true; } } private void addLayerTypeListener(Animator animator, final View view) { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { view.setLayerType(LAYER_TYPE_NONE, null); } @Override public void onAnimationStart(Animator animation) { view.setLayerType(LAYER_TYPE_HARDWARE, null); } }); } } @Override protected void onFinishInflate() { super.onFinishInflate(); bringChildToFront(mAddButton); mButtonsCount = getChildCount(); if (mLabelsStyle != 0) { createLabels(); } } private void createLabels() { Context context = new ContextThemeWrapper(getContext(), mLabelsStyle); for (int i = 0; i < mButtonsCount; i++) { FloatingActionButton button = (FloatingActionButton) getChildAt(i); String title = button.getTitle(); if (button == mAddButton || title == null || button.getTag(R.id.fab_label) != null) continue; TextView label = new TextView(context); label.setTextAppearance(getContext(), mLabelsStyle); label.setText(button.getTitle()); addView(label); button.setTag(R.id.fab_label, label); } } public void collapse() { collapse(false); } public void collapseImmediately() { collapse(true); } private void collapse(boolean immediately) { if (mExpanded) { mExpanded = false; mTouchDelegateGroup.setEnabled(false); mCollapseAnimation.setDuration(immediately ? 0 : ANIMATION_DURATION); mCollapseAnimation.start(); mExpandAnimation.cancel(); if (mListener != null) { mListener.onMenuCollapsed(); } } } public void toggle() { if (mExpanded) { collapse(); } else { expand(); } } public void expand() { if (!mExpanded) { mExpanded = true; mTouchDelegateGroup.setEnabled(true); mCollapseAnimation.cancel(); mExpandAnimation.start(); if (mListener != null) { mListener.onMenuExpanded(); } } } public boolean isExpanded() { return mExpanded; } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); mAddButton.setEnabled(enabled); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.mExpanded = mExpanded; return savedState; } @Override public void onRestoreInstanceState(Parcelable state) { if (state instanceof SavedState) { SavedState savedState = (SavedState) state; mExpanded = savedState.mExpanded; mTouchDelegateGroup.setEnabled(mExpanded); if (mRotatingDrawable != null) { mRotatingDrawable.setRotation(mExpanded ? EXPANDED_PLUS_ROTATION : COLLAPSED_PLUS_ROTATION); } super.onRestoreInstanceState(savedState.getSuperState()); } else { super.onRestoreInstanceState(state); } } public static class SavedState extends BaseSavedState { public boolean mExpanded; public SavedState(Parcelable parcel) { super(parcel); } private SavedState(Parcel in) { super(in); mExpanded = in.readInt() == 1; } @Override public void writeToParcel(@NonNull Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(mExpanded ? 1 : 0); } public static final Creator CREATOR = new Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } public void attachToListView(@NonNull AbsListView listView, ScrollDirectionListener scrollDirectionListener, AbsListView.OnScrollListener onScrollListener) { AbsListViewScrollDetectorImpl scrollDetector = new AbsListViewScrollDetectorImpl(); scrollDetector.setScrollDirectionListener(scrollDirectionListener); scrollDetector.setOnScrollListener(onScrollListener); scrollDetector.setListView(listView); scrollDetector.setScrollThreshold(mScrollThreshold); listView.setOnScrollListener(scrollDetector); } private class AbsListViewScrollDetectorImpl extends AbsListViewScrollDetector { private ScrollDirectionListener mScrollDirectionListener; private AbsListView.OnScrollListener mOnScrollListener; private void setScrollDirectionListener(ScrollDirectionListener scrollDirectionListener) { mScrollDirectionListener = scrollDirectionListener; } public void setOnScrollListener(AbsListView.OnScrollListener onScrollListener) { mOnScrollListener = onScrollListener; } @Override public void onScrollDown() { show(); if (mScrollDirectionListener != null) { mScrollDirectionListener.onScrollDown(); } } @Override public void onScrollUp() { hide(); if (mScrollDirectionListener != null) { mScrollDirectionListener.onScrollUp(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (mOnScrollListener != null) { mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } super.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (mOnScrollListener != null) { mOnScrollListener.onScrollStateChanged(view, scrollState); } super.onScrollStateChanged(view, scrollState); } } public void show() { show(true); } public void hide() { hide(true); } public void show(boolean animate) { toggle(true, animate, false); } public void hide(boolean animate) { toggle(false, animate, false); } private void toggle(final boolean visible, final boolean animate, boolean force) { if (mVisible != visible || force) { mVisible = visible; int height = getHeight(); if (height == 0 && !force) { ViewTreeObserver vto = getViewTreeObserver(); if (vto.isAlive()) { vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { ViewTreeObserver currentVto = getViewTreeObserver(); if (currentVto.isAlive()) { currentVto.removeOnPreDrawListener(this); } toggle(visible, animate, true); return true; } }); return; } } int translationY = visible ? 0 : height + getMarginBottom(); if (animate) { ViewPropertyAnimator.animate(this).setInterpolator(mInterpolator) .setDuration(TRANSLATE_DURATION_MILLIS) .translationY(translationY); } else { ViewHelper.setTranslationY(this, translationY); } // On pre-Honeycomb a translated view is still clickable, so we need to disable clicks manually if (!hasHoneycombApi()) { setClickable(visible); } } } private int getMarginBottom() { int marginBottom = 0; final ViewGroup.LayoutParams layoutParams = getLayoutParams(); if (layoutParams instanceof ViewGroup.MarginLayoutParams) { marginBottom = ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin; } return marginBottom; } private boolean hasHoneycombApi() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/floatbutton/FloatingActionsMenuHidable.java ================================================ package com.hankkin.compustrading.view.floatbutton; import android.content.Context; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.view.ViewGroup; import android.widget.AbsListView; public class FloatingActionsMenuHidable extends FloatingActionsMenu { private boolean isShown = true; private int ANIM_DURATION = 200; private boolean mVisible = false; public FloatingActionsMenuHidable(Context context) { super(context); } public FloatingActionsMenuHidable(Context context, AttributeSet attrs) { super(context, attrs); } public FloatingActionsMenuHidable(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void show(boolean isVisible) { mVisible = isVisible; int translationX = isVisible ? 0 : (getWidth()/2) + getMarginRight(); this.animate().translationX(translationX).setDuration(ANIM_DURATION).start(); } public boolean isShown() { return isShown; } private int getMarginRight() { int marginBottom = 0; final ViewGroup.LayoutParams layoutParams = getLayoutParams(); if (layoutParams instanceof ViewGroup.MarginLayoutParams) { marginBottom = ((MarginLayoutParams) layoutParams).rightMargin; } return marginBottom; } public boolean getVisible(){ return mVisible; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/floatbutton/TouchDelegateGroup.java ================================================ package com.hankkin.compustrading.view.floatbutton; import android.graphics.Rect; import android.support.annotation.NonNull; import android.view.MotionEvent; import android.view.TouchDelegate; import android.view.View; import java.util.ArrayList; public class TouchDelegateGroup extends TouchDelegate { private static final Rect USELESS_HACKY_RECT = new Rect(); private final ArrayList mTouchDelegates = new ArrayList(); private TouchDelegate mCurrentTouchDelegate; private boolean mEnabled; public TouchDelegateGroup(View uselessHackyView) { super(USELESS_HACKY_RECT, uselessHackyView); } public void addTouchDelegate(@NonNull TouchDelegate touchDelegate) { mTouchDelegates.add(touchDelegate); } public void removeTouchDelegate(TouchDelegate touchDelegate) { mTouchDelegates.remove(touchDelegate); if (mCurrentTouchDelegate == touchDelegate) { mCurrentTouchDelegate = null; } } public void clearTouchDelegates() { mTouchDelegates.clear(); mCurrentTouchDelegate = null; } @Override public boolean onTouchEvent(@NonNull MotionEvent event) { if (!mEnabled) return false; TouchDelegate delegate = null; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: for (int i = 0; i < mTouchDelegates.size(); i++) { TouchDelegate touchDelegate = mTouchDelegates.get(i); if (touchDelegate.onTouchEvent(event)) { mCurrentTouchDelegate = touchDelegate; return true; } } break; case MotionEvent.ACTION_MOVE: delegate = mCurrentTouchDelegate; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: delegate = mCurrentTouchDelegate; mCurrentTouchDelegate = null; break; } return delegate != null && delegate.onTouchEvent(event); } public void setEnabled(boolean enabled) { mEnabled = enabled; } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/ipulltozoom/IPullToZoom.java ================================================ package com.hankkin.compustrading.view; /** * Author: ZhuWenWu * Version V1.0 * Date: 2014/11/7 14:21. * Description: * Modification History: * Date Author Version Description * ----------------------------------------------------------------------------------- * 2014/11/7 ZhuWenWu 1.0 1.0 * Why & What is modified: */ import android.content.res.TypedArray; import android.view.View; public interface IPullToZoom { /** * Get the Wrapped Zoom View. Anything returned here has already been * added to the content view. * * @return The View which is currently wrapped */ public View getZoomView(); public View getHeaderView(); /** * Get the Wrapped root View. * * @return The View which is currently wrapped */ public T getPullRootView(); /** * Whether Pull-to-Refresh is enabled * * @return enabled */ public boolean isPullToZoomEnabled(); /** * Returns whether the Widget is currently in the Zooming state * * @return true if the Widget is currently zooming */ public boolean isZooming(); /** * Returns whether the Widget is currently in the Zooming anim type * * @return true if the anim is parallax */ public boolean isParallax(); public boolean isHideHeader(); public void handleStyledAttributes(TypedArray a); } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/ipulltozoom/PullToZoomBase.java ================================================ package com.hankkin.compustrading.view; import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.LinearLayout; import com.hankkin.compustrading.R; /** * Author: ZhuWenWu * Version V1.0 * Date: 2014/11/7 14:18. * Description: * Modification History: * Date Author Version Description * ----------------------------------------------------------------------------------- * 2014/11/7 ZhuWenWu 1.0 1.0 * Why & What is modified: */ public abstract class PullToZoomBase extends LinearLayout implements IPullToZoom { private static final float FRICTION = 2.0f; protected T mRootView; protected View mHeaderView;//头部View protected View mZoomView;//缩放拉伸View protected int mScreenHeight; protected int mScreenWidth; private boolean isZoomEnabled = true; private boolean isParallax = true; private boolean isZooming = false; private boolean isHideHeader = false; private int mTouchSlop; private boolean mIsBeingDragged = false; private float mLastMotionY; private float mLastMotionX; private float mInitialMotionY; private float mInitialMotionX; private OnPullZoomListener onPullZoomListener; public PullToZoomBase(Context context) { this(context, null); } public PullToZoomBase(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } private void init(Context context, AttributeSet attrs) { setGravity(Gravity.CENTER); ViewConfiguration config = ViewConfiguration.get(context); mTouchSlop = config.getScaledTouchSlop(); DisplayMetrics localDisplayMetrics = new DisplayMetrics(); ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics); mScreenHeight = localDisplayMetrics.heightPixels; mScreenWidth = localDisplayMetrics.widthPixels; // Refreshable View // By passing the attrs, we can add ListView/GridView params via XML mRootView = createRootView(context, attrs); if (attrs != null) { LayoutInflater mLayoutInflater = LayoutInflater.from(getContext()); //初始化状态View TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.PullToZoomView); int zoomViewResId = a.getResourceId(R.styleable.PullToZoomView_zoomView, 0); if (zoomViewResId > 0) { mZoomView = mLayoutInflater.inflate(zoomViewResId, null, false); } int headerViewResId = a.getResourceId(R.styleable.PullToZoomView_headerView, 0); if (headerViewResId > 0) { mHeaderView = mLayoutInflater.inflate(headerViewResId, null, false); } isParallax = a.getBoolean(R.styleable.PullToZoomView_isHeaderParallax, true); // Let the derivative classes have a go at handling attributes, then // recycle them... handleStyledAttributes(a); a.recycle(); } addView(mRootView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); } public void setOnPullZoomListener(OnPullZoomListener onPullZoomListener) { this.onPullZoomListener = onPullZoomListener; } @Override public T getPullRootView() { return mRootView; } @Override public View getZoomView() { return mZoomView; } @Override public View getHeaderView() { return mHeaderView; } @Override public boolean isPullToZoomEnabled() { return isZoomEnabled; } @Override public boolean isZooming() { return isZooming; } @Override public boolean isParallax() { return isParallax; } @Override public boolean isHideHeader() { return isHideHeader; } public void setZoomEnabled(boolean isZoomEnabled) { this.isZoomEnabled = isZoomEnabled; } public void setParallax(boolean isParallax) { this.isParallax = isParallax; } public void setHideHeader(boolean isHideHeader) {//header显示才能Zoom this.isHideHeader = isHideHeader; } @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (!isPullToZoomEnabled() || isHideHeader()) { return false; } final int action = event.getAction(); if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { mIsBeingDragged = false; return false; } if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) { return true; } switch (action) { case MotionEvent.ACTION_MOVE: { if (isReadyForPullStart()) { final float y = event.getY(), x = event.getX(); final float diff, oppositeDiff, absDiff; // We need to use the correct values, based on scroll // direction diff = y - mLastMotionY; oppositeDiff = x - mLastMotionX; absDiff = Math.abs(diff); if (absDiff > mTouchSlop && absDiff > Math.abs(oppositeDiff)) { if (diff >= 1f && isReadyForPullStart()) { mLastMotionY = y; mLastMotionX = x; mIsBeingDragged = true; } } } break; } case MotionEvent.ACTION_DOWN: { if (isReadyForPullStart()) { mLastMotionY = mInitialMotionY = event.getY(); mLastMotionX = mInitialMotionX = event.getX(); mIsBeingDragged = false; } break; } } return mIsBeingDragged; } @Override public boolean onTouchEvent(@NonNull MotionEvent event) { if (!isPullToZoomEnabled() || isHideHeader()) { return false; } if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) { return false; } switch (event.getAction()) { case MotionEvent.ACTION_MOVE: { if (mIsBeingDragged) { mLastMotionY = event.getY(); mLastMotionX = event.getX(); pullEvent(); isZooming = true; return true; } break; } case MotionEvent.ACTION_DOWN: { if (isReadyForPullStart()) { mLastMotionY = mInitialMotionY = event.getY(); mLastMotionX = mInitialMotionX = event.getX(); return true; } break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { if (mIsBeingDragged) { mIsBeingDragged = false; // If we're already refreshing, just scroll back to the top if (isZooming()) { smoothScrollToTop(); if (onPullZoomListener != null) { onPullZoomListener.onPullZoomEnd(); } isZooming = false; return true; } return true; } break; } } return false; } private void pullEvent() { final int newScrollValue; final float initialMotionValue, lastMotionValue; initialMotionValue = mInitialMotionY; lastMotionValue = mLastMotionY; newScrollValue = Math.round(Math.min(initialMotionValue - lastMotionValue, 0) / FRICTION); pullHeaderToZoom(newScrollValue); if (onPullZoomListener != null) { onPullZoomListener.onPullZooming(newScrollValue); } } protected abstract void pullHeaderToZoom(int newScrollValue); public abstract void setHeaderView(View headerView); public abstract void setZoomView(View zoomView); protected abstract T createRootView(Context context, AttributeSet attrs); protected abstract void smoothScrollToTop(); protected abstract boolean isReadyForPullStart(); public interface OnPullZoomListener { public void onPullZooming(int newScrollValue); public void onPullZoomEnd(); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/ipulltozoom/PullToZoomScrollViewEx.java ================================================ package com.hankkin.compustrading.view; import android.content.Context; import android.content.res.TypedArray; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ScrollView; import com.hankkin.compustrading.R; /** * Author: ZhuWenWu * Version V1.0 * Date: 2014/11/10 14:25. * Description: * Modification History: * Date Author Version Description * ----------------------------------------------------------------------------------- * 2014/11/10 ZhuWenWu 1.0 1.0 * Why & What is modified: */ public class PullToZoomScrollViewEx extends PullToZoomBase { private static final String TAG = PullToZoomScrollViewEx.class.getSimpleName(); private boolean isCustomHeaderHeight = false; private FrameLayout mHeaderContainer; private LinearLayout mRootContainer; private View mContentView; private int mHeaderHeight; private ScalingRunnable mScalingRunnable; private static final Interpolator sInterpolator = new Interpolator() { public float getInterpolation(float paramAnonymousFloat) { float f = paramAnonymousFloat - 1.0F; return 1.0F + f * (f * (f * (f * f))); } }; public PullToZoomScrollViewEx(Context context) { this(context, null); } public PullToZoomScrollViewEx(Context context, AttributeSet attrs) { super(context, attrs); mScalingRunnable = new ScalingRunnable(); ((InternalScrollView) mRootView).setOnScrollViewChangedListener(new OnScrollViewChangedListener() { @Override public void onInternalScrollChanged(int left, int top, int oldLeft, int oldTop) { if (isPullToZoomEnabled() && isParallax()) { Log.d(TAG, "onScrollChanged --> getScrollY() = " + mRootView.getScrollY()); float f = mHeaderHeight - mHeaderContainer.getBottom() + mRootView.getScrollY(); Log.d(TAG, "onScrollChanged --> f = " + f); if ((f > 0.0F) && (f < mHeaderHeight)) { int i = (int) (0.65D * f); mHeaderContainer.scrollTo(0, -i); } else if (mHeaderContainer.getScrollY() != 0) { mHeaderContainer.scrollTo(0, 0); } } } }); } @Override protected void pullHeaderToZoom(int newScrollValue) { Log.d(TAG, "pullHeaderToZoom --> newScrollValue = " + newScrollValue); Log.d(TAG, "pullHeaderToZoom --> mHeaderHeight = " + mHeaderHeight); if (mScalingRunnable != null && !mScalingRunnable.isFinished()) { mScalingRunnable.abortAnimation(); } ViewGroup.LayoutParams localLayoutParams = mHeaderContainer.getLayoutParams(); localLayoutParams.height = Math.abs(newScrollValue) + mHeaderHeight; mHeaderContainer.setLayoutParams(localLayoutParams); if (isCustomHeaderHeight) { ViewGroup.LayoutParams zoomLayoutParams = mZoomView.getLayoutParams(); zoomLayoutParams.height = Math.abs(newScrollValue) + mHeaderHeight; mZoomView.setLayoutParams(zoomLayoutParams); } } /** * 是否显示headerView * * @param isHideHeader true: show false: hide */ @Override public void setHideHeader(boolean isHideHeader) { if (isHideHeader != isHideHeader() && mHeaderContainer != null) { super.setHideHeader(isHideHeader); if (isHideHeader) { mHeaderContainer.setVisibility(GONE); } else { mHeaderContainer.setVisibility(VISIBLE); } } } @Override public void setHeaderView(View headerView) { if (headerView != null) { mHeaderView = headerView; updateHeaderView(); } } @Override public void setZoomView(View zoomView) { if (zoomView != null) { mZoomView = zoomView; updateHeaderView(); } } private void updateHeaderView() { if (mHeaderContainer != null) { mHeaderContainer.removeAllViews(); if (mZoomView != null) { mHeaderContainer.addView(mZoomView); } if (mHeaderView != null) { mHeaderContainer.addView(mHeaderView); } } } public void setScrollContentView(View contentView) { if (contentView != null) { if (mContentView != null) { mRootContainer.removeView(mContentView); } mContentView = contentView; mRootContainer.addView(mContentView); } } @Override protected ScrollView createRootView(Context context, AttributeSet attrs) { ScrollView scrollView = new InternalScrollView(context, attrs); scrollView.setId(R.id.scrollview); return scrollView; } @Override protected void smoothScrollToTop() { Log.d(TAG, "smoothScrollToTop --> "); mScalingRunnable.startAnimation(200L); } @Override protected boolean isReadyForPullStart() { return mRootView.getScrollY() == 0; } @Override public void handleStyledAttributes(TypedArray a) { mRootContainer = new LinearLayout(getContext()); mRootContainer.setOrientation(LinearLayout.VERTICAL); mHeaderContainer = new FrameLayout(getContext()); if (mZoomView != null) { mHeaderContainer.addView(mZoomView); } if (mHeaderView != null) { mHeaderContainer.addView(mHeaderView); } int contentViewResId = a.getResourceId(R.styleable.PullToZoomView_contentView, 0); if (contentViewResId > 0) { LayoutInflater mLayoutInflater = LayoutInflater.from(getContext()); mContentView = mLayoutInflater.inflate(contentViewResId, null, false); } mRootContainer.addView(mHeaderContainer); if (mContentView != null) { mRootContainer.addView(mContentView); } mRootContainer.setClipChildren(false); mHeaderContainer.setClipChildren(false); mRootView.addView(mRootContainer); } /** * 设置HeaderView高度 * * @param width * @param height */ public void setHeaderViewSize(int width, int height) { if (mHeaderContainer != null) { Object localObject = mHeaderContainer.getLayoutParams(); if (localObject == null) { localObject = new ViewGroup.LayoutParams(width, height); } ((ViewGroup.LayoutParams) localObject).width = width; ((ViewGroup.LayoutParams) localObject).height = height; mHeaderContainer.setLayoutParams((ViewGroup.LayoutParams) localObject); mHeaderHeight = height; isCustomHeaderHeight = true; } } /** * 设置HeaderView LayoutParams * * @param layoutParams LayoutParams */ public void setHeaderLayoutParams(LinearLayout.LayoutParams layoutParams) { if (mHeaderContainer != null) { mHeaderContainer.setLayoutParams(layoutParams); mHeaderHeight = layoutParams.height; isCustomHeaderHeight = true; } } protected void onLayout(boolean paramBoolean, int paramInt1, int paramInt2, int paramInt3, int paramInt4) { super.onLayout(paramBoolean, paramInt1, paramInt2, paramInt3, paramInt4); Log.d(TAG, "onLayout --> "); if (mHeaderHeight == 0 && mZoomView != null) { mHeaderHeight = mHeaderContainer.getHeight(); } } class ScalingRunnable implements Runnable { protected long mDuration; protected boolean mIsFinished = true; protected float mScale; protected long mStartTime; ScalingRunnable() { } public void abortAnimation() { mIsFinished = true; } public boolean isFinished() { return mIsFinished; } public void run() { if (mZoomView != null) { float f2; ViewGroup.LayoutParams localLayoutParams; if ((!mIsFinished) && (mScale > 1.0D)) { float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) mStartTime) / (float) mDuration; f2 = mScale - (mScale - 1.0F) * PullToZoomScrollViewEx.sInterpolator.getInterpolation(f1); localLayoutParams = mHeaderContainer.getLayoutParams(); Log.d(TAG, "ScalingRunnable --> f2 = " + f2); if (f2 > 1.0F) { localLayoutParams.height = ((int) (f2 * mHeaderHeight)); mHeaderContainer.setLayoutParams(localLayoutParams); if (isCustomHeaderHeight) { ViewGroup.LayoutParams zoomLayoutParams; zoomLayoutParams = mZoomView.getLayoutParams(); zoomLayoutParams.height = ((int) (f2 * mHeaderHeight)); mZoomView.setLayoutParams(zoomLayoutParams); } post(this); return; } mIsFinished = true; } } } public void startAnimation(long paramLong) { if (mZoomView != null) { mStartTime = SystemClock.currentThreadTimeMillis(); mDuration = paramLong; mScale = ((float) (mHeaderContainer.getBottom()) / mHeaderHeight); mIsFinished = false; post(this); } } } protected class InternalScrollView extends ScrollView { private OnScrollViewChangedListener onScrollViewChangedListener; public InternalScrollView(Context context) { this(context, null); } public InternalScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public void setOnScrollViewChangedListener(OnScrollViewChangedListener onScrollViewChangedListener) { this.onScrollViewChangedListener = onScrollViewChangedListener; } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (onScrollViewChangedListener != null) { onScrollViewChangedListener.onInternalScrollChanged(l, t, oldl, oldt); } } } protected interface OnScrollViewChangedListener { public void onInternalScrollChanged(int left, int top, int oldLeft, int oldTop); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/refreshload/MetaballView.java ================================================ package com.hankkin.compustrading.view.refreshload; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Animation; import android.view.animation.Transformation; import com.hankkin.compustrading.R; import java.util.ArrayList; /** * Created by rxp on 15/10/23. */ public class MetaballView extends View { private Paint paint = new Paint(); private float handle_len_rate = 2f; private float radius = 30; private final int ITEM_COUNT = 6; private final int ITEM_DIVIDER = 60; private final float SCALE_RATE = 0.3f; private float maxLength; private ArrayList circlePaths = new ArrayList<>(); private float mInterpolatedTime; private MoveAnimation wa; private Circle circle; public MetaballView(Context context) { super(context); init(); } public MetaballView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public MetaballView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private class Circle { float[] center; float radius; } public void setPaintMode(int mode) { paint.setStyle(mode == 0 ? Paint.Style.STROKE : Paint.Style.FILL); invalidate(); } private void init() { paint.setColor(getResources().getColor(R.color.theme_color)); // paint.setColor(0xff4db9ff); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); Circle circlePath = new Circle(); circlePath.center = new float[]{(radius + ITEM_DIVIDER), radius * (1f + SCALE_RATE)}; circlePath.radius = radius / 4 * 3; circlePaths.add(circlePath); for (int i = 1; i < ITEM_COUNT; i++) { circlePath = new Circle(); circlePath.center = new float[]{(radius * 2 + ITEM_DIVIDER) * i, radius * (1f + SCALE_RATE)}; circlePath.radius = radius; circlePaths.add(circlePath); } maxLength = (radius * 2 + ITEM_DIVIDER) * ITEM_COUNT; } private float[] getVector(float radians, float length) { float x = (float) (Math.cos(radians) * length); float y = (float) (Math.sin(radians) * length); return new float[]{ x, y }; } private class MoveAnimation extends Animation { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); mInterpolatedTime = interpolatedTime; invalidate(); } } /** * @param canvas 画布 * @param j * @param i * @param v 控制两个圆连接时候长度,间接控制连接线的粗细,该值为1的时候连接线为直线 * @param handle_len_rate * @param maxDistance */ private void metaball(Canvas canvas, int j, int i, float v, float handle_len_rate, float maxDistance) { final Circle circle1 = circlePaths.get(i); final Circle circle2 = circlePaths.get(j); RectF ball1 = new RectF(); ball1.left = circle1.center[0] - circle1.radius; ball1.top = circle1.center[1] - circle1.radius; ball1.right = ball1.left + circle1.radius * 2; ball1.bottom = ball1.top + circle1.radius * 2; RectF ball2 = new RectF(); ball2.left = circle2.center[0] - circle2.radius; ball2.top = circle2.center[1] - circle2.radius; ball2.right = ball2.left + circle2.radius * 2; ball2.bottom = ball2.top + circle2.radius * 2; float[] center1 = new float[]{ ball1.centerX(), ball1.centerY() }; float[] center2 = new float[]{ ball2.centerX(), ball2.centerY() }; float d = getDistance(center1, center2); float radius1 = ball1.width() / 2; float radius2 = ball2.width() / 2; float pi2 = (float) (Math.PI / 2); float u1, u2; if (d > maxDistance) { // canvas.drawCircle(ball1.centerX(), ball1.centerY(), circle1.radius, paint); canvas.drawCircle(ball2.centerX(), ball2.centerY(), circle2.radius, paint); } else { float scale2 = 1 + SCALE_RATE * (1 - d / maxDistance); float scale1 = 1 - SCALE_RATE * (1 - d / maxDistance); radius2 *= scale2; // radius1 *= scale1; // canvas.drawCircle(ball1.centerX(), ball1.centerY(), radius1, paint); canvas.drawCircle(ball2.centerX(), ball2.centerY(), radius2, paint); } // Log.d("Metaball_radius", "radius1:" + radius1 + ",radius2:" + radius2); if (radius1 == 0 || radius2 == 0) { return; } if (d > maxDistance || d <= Math.abs(radius1 - radius2)) { return; } else if (d < radius1 + radius2) { u1 = (float) Math.acos((radius1 * radius1 + d * d - radius2 * radius2) / (2 * radius1 * d)); u2 = (float) Math.acos((radius2 * radius2 + d * d - radius1 * radius1) / (2 * radius2 * d)); } else { u1 = 0; u2 = 0; } // Log.d("Metaball", "center2:" + Arrays.toString(center2) + ",center1:" + Arrays.toString(center1)); float[] centermin = new float[]{center2[0] - center1[0], center2[1] - center1[1]}; float angle1 = (float) Math.atan2(centermin[1], centermin[0]); float angle2 = (float) Math.acos((radius1 - radius2) / d); float angle1a = angle1 + u1 + (angle2 - u1) * v; float angle1b = angle1 - u1 - (angle2 - u1) * v; float angle2a = (float) (angle1 + Math.PI - u2 - (Math.PI - u2 - angle2) * v); float angle2b = (float) (angle1 - Math.PI + u2 + (Math.PI - u2 - angle2) * v); // Log.d("Metaball", "angle1:" + angle1 + ",angle2:" + angle2 + ",angle1a:" + angle1a + ",angle1b:" + angle1b + ",angle2a:" + angle2a + ",angle2b:" + angle2b); float[] p1a1 = getVector(angle1a, radius1); float[] p1b1 = getVector(angle1b, radius1); float[] p2a1 = getVector(angle2a, radius2); float[] p2b1 = getVector(angle2b, radius2); float[] p1a = new float[]{p1a1[0] + center1[0], p1a1[1] + center1[1]}; float[] p1b = new float[]{p1b1[0] + center1[0], p1b1[1] + center1[1]}; float[] p2a = new float[]{p2a1[0] + center2[0], p2a1[1] + center2[1]}; float[] p2b = new float[]{p2b1[0] + center2[0], p2b1[1] + center2[1]}; // Log.d("Metaball", "p1a:" + Arrays.toString(p1a) + ",p1b:" + Arrays.toString(p1b) + ",p2a:" + Arrays.toString(p2a) + ",p2b:" + Arrays.toString(p2b)); float[] p1_p2 = new float[]{p1a[0] - p2a[0], p1a[1] - p2a[1]}; float totalRadius = (radius1 + radius2); float d2 = Math.min(v * handle_len_rate, getLength(p1_p2) / totalRadius); d2 *= Math.min(1, d * 2 / (radius1 + radius2)); // Log.d("Metaball", "d2:" + d2); radius1 *= d2; radius2 *= d2; float[] sp1 = getVector(angle1a - pi2, radius1); float[] sp2 = getVector(angle2a + pi2, radius2); float[] sp3 = getVector(angle2b - pi2, radius2); float[] sp4 = getVector(angle1b + pi2, radius1); // Log.d("Metaball", "sp1:" + Arrays.toString(sp1) + ",sp2:" + Arrays.toString(sp2) + ",sp3:" + Arrays.toString(sp3) + ",sp4:" + Arrays.toString(sp4)); Path path1 = new Path(); path1.moveTo(p1a[0], p1a[1]); path1.cubicTo(p1a[0] + sp1[0], p1a[1] + sp1[1], p2a[0] + sp2[0], p2a[1] + sp2[1], p2a[0], p2a[1]); path1.lineTo(p2b[0], p2b[1]); path1.cubicTo(p2b[0] + sp3[0], p2b[1] + sp3[1], p1b[0] + sp4[0], p1b[1] + sp4[1], p1b[0], p1b[1]); path1.lineTo(p1a[0], p1a[1]); path1.close(); canvas.drawPath(path1, paint); } private float getLength(float[] b) { return (float) Math.sqrt(b[0] * b[0] + b[1] * b[1]); } private float getDistance(float[] b1, float[] b2) { float x = b1[0] - b2[0]; float y = b1[1] - b2[1]; float d = x * x + y * y; return (float) Math.sqrt(d); } //测试用 // @Override // public boolean onTouchEvent(MotionEvent event) { // switch (event.getAction()) { // case MotionEvent.ACTION_DOWN: // break; // case MotionEvent.ACTION_MOVE: // Circle circle = circlePaths.get(0); // circle.center[0] = event.getX(); // circle.center[1] = event.getY(); // invalidate(); // break; // case MotionEvent.ACTION_UP: // break; // } // // return true; // } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); circle = circlePaths.get(0); circle.center[0] = maxLength * mInterpolatedTime; RectF ball1 = new RectF(); ball1.left = circle.center[0] - circle.radius; ball1.top = circle.center[1] - circle.radius; ball1.right = ball1.left + circle.radius * 2; ball1.bottom = ball1.top + circle.radius * 2; canvas.drawCircle(ball1.centerX(), ball1.centerY(), circle.radius, paint); for (int i = 1, l = circlePaths.size(); i < l; i++) { metaball(canvas, i, 0, 0.6f, handle_len_rate, radius * 4f); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(resolveSizeAndState((int) (ITEM_COUNT * (radius * 2 + ITEM_DIVIDER)), widthMeasureSpec, 0), resolveSizeAndState((int) (2 * radius * 1.4f), heightMeasureSpec, 0)); } private void stopAnimation() { this.clearAnimation(); postInvalidate(); } private void startAnimation() { wa = new MoveAnimation(); wa.setDuration(2500); wa.setInterpolator(new AccelerateDecelerateInterpolator()); wa.setRepeatCount(Animation.INFINITE); wa.setRepeatMode(Animation.REVERSE); startAnimation(wa); } @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if (visibility == GONE || visibility == INVISIBLE) { stopAnimation(); } else { startAnimation(); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); startAnimation(); } @Override protected void onDetachedFromWindow() { stopAnimation(); super.onDetachedFromWindow(); } } ================================================ FILE: app/src/main/java/com/hankkin/compustrading/view/refreshload/RefreshLayout.java ================================================ package com.hankkin.compustrading.view; import android.content.Context; import android.support.v4.widget.SwipeRefreshLayout; import android.util.AttributeSet; import android.view.Gravity; import android.view.MotionEvent; import android.widget.LinearLayout; import android.widget.ListView; import com.hankkin.compustrading.view.refreshload.MetaballView; public class RefreshLayout extends SwipeRefreshLayout { private final int mTouchSlop; private ListView mListView; private OnLoadListener mOnLoadListener; private float firstTouchY; private float lastTouchY; private boolean isLoading = false; private MetaballView footerView; private LinearLayout footerHolder; public RefreshLayout(Context context) { this(context, null); } public RefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); // mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mTouchSlop = 200; footerView = new MetaballView(context,attrs); // footerView.setPaintMode(0); footerHolder = new LinearLayout(context); footerHolder.setGravity(Gravity.CENTER); footerHolder.addView(footerView); footerView.setVisibility(GONE); } //set the child view of RefreshLayout,ListView public void setChildView(ListView mListView) { this.mListView = mListView; mListView.addFooterView(footerHolder); } @Override public boolean dispatchTouchEvent(MotionEvent event) { final int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: firstTouchY = event.getRawY(); break; case MotionEvent.ACTION_UP: lastTouchY = event.getRawY(); if (canLoadMore()) { loadData(); } // Log.d("movvvvvvve", firstTouchY - lastTouchY + ""); // // Log.d("movvve",mTouchSlop +""); break; default: break; } return super.dispatchTouchEvent(event); } private boolean canLoadMore() { return isBottom() && !isLoading && isPullingUp(); } private boolean isBottom() { if (mListView.getCount() > 0) { if (mListView.getLastVisiblePosition() == mListView.getAdapter().getCount() - 1 && mListView.getChildAt(mListView.getChildCount() - 1).getBottom() <= mListView.getHeight()) { return true; } } return false; } private boolean isPullingUp() { return (firstTouchY - lastTouchY) >= mTouchSlop; } private void loadData() { if (mOnLoadListener != null) { setLoading(true); } } public void setLoading(boolean loading) { if (mListView == null) return; isLoading = loading; if (loading) { if (isRefreshing()) { setRefreshing(false); } footerView.setVisibility(VISIBLE); mListView.setSelection(mListView.getAdapter().getCount() - 1); mOnLoadListener.onLoad(); } else { footerView.setVisibility(GONE); firstTouchY = 0; lastTouchY = 0; } } public void setOnLoadListener(OnLoadListener loadListener) { mOnLoadListener = loadListener; } public interface OnLoadListener { public void onLoad(); } } ================================================ FILE: app/src/main/res/anim/anim.xml ================================================ ================================================ FILE: app/src/main/res/anim/list_anim.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bmob_update_button_cancel_bg_focused.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bmob_update_button_cancel_bg_normal.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bmob_update_button_cancel_bg_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bmob_update_button_cancel_bg_tap.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bmob_update_button_check_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bmob_update_button_close_bg_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bmob_update_button_ok_bg_focused.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bmob_update_button_ok_bg_normal.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bmob_update_button_ok_bg_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bmob_update_button_ok_bg_tap.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bmob_update_dialog_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/btn_login_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable/fab_label_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable/login_et_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable/normal_white_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/shadow.xml ================================================ ================================================ FILE: app/src/main/res/drawable/tab_background.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_login.xml ================================================