diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..915cc92 --- /dev/null +++ b/.gitignore @@ -0,0 +1,99 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +# Created by https://www.gitignore.io + +### Android ### +# Built application files +/*/*.apk +*.ap_ + +# Files for the Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ + +# Gradle files +.gradle/ +build/ +/*/build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + + +### Gradle ### +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + + +# Created by https://www.gitignore.io + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +*.iml +app.iml +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties \ No newline at end of file diff --git a/FloatViewFinal-Sample.apk b/FloatViewFinal-Sample.apk new file mode 100644 index 0000000..90c469c Binary files /dev/null and b/FloatViewFinal-Sample.apk differ diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..77c1387 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 pengjianbo(pengjianbosoft@gmail.com), Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + + defaultConfig { + applicationId "cn.finalteam.floatviewfinal.sample" + minSdkVersion 7 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + testCompile 'junit:junit:4.12' + compile 'com.android.support:appcompat-v7:23.1.0' + compile 'com.android.support:design:23.1.0' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..b0e2773 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/pengjianbo/Documents/dev/android_dev/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 *; +#} diff --git a/app/src/androidTest/java/cn/finalteam/floatviewfinal/sample/ApplicationTest.java b/app/src/androidTest/java/cn/finalteam/floatviewfinal/sample/ApplicationTest.java new file mode 100644 index 0000000..308ba8c --- /dev/null +++ b/app/src/androidTest/java/cn/finalteam/floatviewfinal/sample/ApplicationTest.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 pengjianbo(pengjianbosoft@gmail.com), Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.finalteam.floatviewfinal.sample; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b47315c --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/cn/finalteam/floatviewfinal/sample/MainActivity.java b/app/src/main/java/cn/finalteam/floatviewfinal/sample/MainActivity.java new file mode 100644 index 0000000..8337687 --- /dev/null +++ b/app/src/main/java/cn/finalteam/floatviewfinal/sample/MainActivity.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2015 pengjianbo(pengjianbosoft@gmail.com), Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.finalteam.floatviewfinal.sample; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import cn.finalteam.floatviewfinal.service.FloatViewService; + +public class MainActivity extends AppCompatActivity { + + private FloatViewService mFloatViewService; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) + .setAction("Action", null).show(); + } + }); + + Button btnShowFloat = (Button) findViewById(R.id.btn_show_float); + btnShowFloat.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + showFloatingView(); + } + }); + + Button btnHideFloat = (Button) findViewById(R.id.btn_hide_float); + btnHideFloat.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + hideFloatingView(); + } + }); + + try { + Intent intent = new Intent(this, FloatViewService.class); + startService(intent); + bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); + } catch (Exception e) { + } + } + + @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) { + // 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); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if ( keyCode == KeyEvent.KEYCODE_BACK ) { + finish(); + } + return super.onKeyDown(keyCode, event); + } + + @Override + protected void onDestroy() { + destroy(); + super.onDestroy(); + } + + /** + * 显示悬浮图标 + */ + public void showFloatingView() { + if ( mFloatViewService != null ) { + mFloatViewService.showFloat(); + } + } + + /** + * 隐藏悬浮图标 + */ + public void hideFloatingView() { + if ( mFloatViewService != null ) { + mFloatViewService.hideFloat(); + } + } + + /** + * 释放PJSDK数据 + */ + public void destroy() { + try { + stopService(new Intent(this, FloatViewService.class)); + unbindService(mServiceConnection); + } catch (Exception e) { + } + } + + /** + * 连接到Service + */ + private final ServiceConnection mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName componentName, IBinder iBinder) { + mFloatViewService = ((FloatViewService.FloatViewServiceBinder) iBinder).getService(); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + mFloatViewService = null; + } + }; +} diff --git a/app/src/main/java/cn/finalteam/floatviewfinal/service/FloatViewService.java b/app/src/main/java/cn/finalteam/floatviewfinal/service/FloatViewService.java new file mode 100644 index 0000000..ddabe0b --- /dev/null +++ b/app/src/main/java/cn/finalteam/floatviewfinal/service/FloatViewService.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2015 pengjianbo(pengjianbosoft@gmail.com), Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.finalteam.floatviewfinal.service; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.support.annotation.Nullable; +import cn.finalteam.floatviewfinal.widget.FloatView; +import java.lang.ref.WeakReference; + +/** + * Desction:Float view service + * Author:pengjianbo + * Date:15/10/26 下午5:15 + */ +public class FloatViewService extends Service{ + + private FloatView mFloatView; + private IBinder mFloatViewServiceBinder; + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return mFloatViewServiceBinder; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (mFloatView != null) { + return START_STICKY; + } + + mFloatView = new FloatView(this); + mFloatViewServiceBinder = new FloatViewServiceBinder(this); + + return START_REDELIVER_INTENT; + } + + public void showFloat() { + if ( mFloatView != null ) { + mFloatView.show(); + } + } + + public void hideFloat() { + if ( mFloatView != null ) { + mFloatView.hide(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if ( mFloatView != null ) { + mFloatView.destroy(); + } + } + + public class FloatViewServiceBinder extends Binder { + + private final WeakReference mService; + + FloatViewServiceBinder(FloatViewService service) { + mService = new WeakReference<>(service); + } + + public FloatViewService getService() { + return mService.get(); + } + } +} diff --git a/app/src/main/java/cn/finalteam/floatviewfinal/utils/ResourceUtils.java b/app/src/main/java/cn/finalteam/floatviewfinal/utils/ResourceUtils.java new file mode 100644 index 0000000..c490e4e --- /dev/null +++ b/app/src/main/java/cn/finalteam/floatviewfinal/utils/ResourceUtils.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2015 pengjianbo(pengjianbosoft@gmail.com), Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.finalteam.floatviewfinal.utils; + +import android.content.Context; + +/** + * Desction: + * Author:pengjianbo + * Date:15/10/22 下午9:01 + */ +public class ResourceUtils { + /** + * 获取 layout 布局文件 + * @param context Context + * @param resName layout xml 的文件名 + * @return layout + */ + public static int getLayoutId(Context context, String resName) { + return context.getResources().getIdentifier(resName, "layout", + context.getPackageName()); + } + + /** + * 获取 string 值 + * @param context Context + * @param resName string name的名称 + * @return string + */ + public static int getStringId(Context context, String resName) { + return context.getResources().getIdentifier(resName, "string", + context.getPackageName()); + } + + /** + * 获取 drawable 布局文件 或者 图片的 + * @param context Context + * @param resName drawable 的名称 + * @return drawable + */ + public static int getDrawableId(Context context, String resName) { + return context.getResources().getIdentifier(resName, + "drawable", context.getPackageName()); + } + + + /** + * 获取 style + * @param context Context + * @param resName style的名称 + * @return style + */ + public static int getStyleId(Context context, String resName) { + return context.getResources().getIdentifier(resName, "style", + context.getPackageName()); + } + + /** + * 获取 styleable + * @param context Context + * @param resName styleable 的名称 + * @return styleable + */ + public static Object getStyleableId(Context context, String resName){ + return context.getResources().getIdentifier(resName, "styleable", + context.getPackageName()); + } + + + /** + * 获取 anim + * @param context Context + * @param resName anim xml 文件名称 + * @return anim + */ + public static int getAnimId(Context context, String resName) { + return context.getResources().getIdentifier(resName, "anim", + context.getPackageName()); + } + + /** + * 获取 id + * @param context Context + * @param resName id 的名称 + * @return + */ + public static int getId(Context context, String resName) { + return context.getResources().getIdentifier(resName, "id", + context.getPackageName()); + } + + /** + * color + * @param context Context + * @param resName color 名称 + * @return + */ + public static int getColorId(Context context, String resName) { + return context.getResources().getIdentifier(resName, "color", + context.getPackageName()); + } +} diff --git a/app/src/main/java/cn/finalteam/floatviewfinal/widget/FloatView.java b/app/src/main/java/cn/finalteam/floatviewfinal/widget/FloatView.java new file mode 100644 index 0000000..999d311 --- /dev/null +++ b/app/src/main/java/cn/finalteam/floatviewfinal/widget/FloatView.java @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2015 pengjianbo(pengjianbosoft@gmail.com), Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.finalteam.floatviewfinal.widget; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.PixelFormat; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.LinearInterpolator; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import cn.finalteam.floatviewfinal.utils.ResourceUtils; +import java.util.Timer; +import java.util.TimerTask; + +/** + * Desction:悬浮窗 + * Author:pengjianbo + * Date:15/10/26 下午8:39 + */ +public class FloatView extends FrameLayout implements OnTouchListener { + + private final int HANDLER_TYPE_TIMER = 100; + + private WindowManager.LayoutParams mWmParams; + private WindowManager mWindowManager; + private Context mContext; + + private View mRootFloatView; + private ImageView mIvFloatLogo; + private ImageView mIvFloatLoader; + private LinearLayout mLlFloatMenu; + private TextView mTvAccount; + private TextView mTvFeedback; + private FrameLayout mFlFloatLogo; + + private boolean mIsRight;//logo是否在右边 + private boolean mCanHide;//是否允许隐藏 + private float mTouchStartX; + private float mTouchStartY; + private int mScreenWidth; + private int mScreenHeight; + private boolean mDraging; + + private Timer mTimer; + private TimerTask mTimerTask; + + final Handler mTimerHandler = new Handler() { + public void handleMessage(Message msg) { + if ( msg.what == HANDLER_TYPE_TIMER ) { + // 比如隐藏悬浮框 + if (mCanHide) { + mCanHide = false; + if (mIsRight) { + mIvFloatLogo.setImageResource(ResourceUtils.getDrawableId( + mContext, "pj_image_float_right")); + mIvFloatLoader.clearAnimation(); + mIvFloatLoader.setVisibility(View.GONE); + } else { + mIvFloatLogo.setImageResource(ResourceUtils.getDrawableId( + mContext, "pj_image_float_left")); + mIvFloatLoader.clearAnimation(); + mIvFloatLoader.setVisibility(View.GONE); + } + mWmParams.alpha = 0.7f; + mWindowManager.updateViewLayout(FloatView.this, mWmParams); + refreshFloatMenu(mIsRight); + mLlFloatMenu.setVisibility(View.GONE); + } + } + super.handleMessage(msg); + } + }; + + public FloatView(Context context) { + super(context); + init(context); + } + + private void init(Context mContext) { + this.mContext = mContext; + + mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + // 更新浮动窗口位置参数 靠边 + DisplayMetrics dm = new DisplayMetrics(); + // 获取屏幕信息 + mWindowManager.getDefaultDisplay().getMetrics(dm); + mScreenWidth = dm.widthPixels; + mScreenHeight = dm.heightPixels; + this.mWmParams = new WindowManager.LayoutParams(); + // 设置window type + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + mWmParams.type = WindowManager.LayoutParams.TYPE_TOAST; + } else { + mWmParams.type = WindowManager.LayoutParams.TYPE_PHONE; + } + // 设置图片格式,效果为背景透明 + mWmParams.format = PixelFormat.RGBA_8888; + // 设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作) + mWmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + // 调整悬浮窗显示的停靠位置为左侧置�? + mWmParams.gravity = Gravity.LEFT | Gravity.TOP; + + mScreenHeight = mWindowManager.getDefaultDisplay().getHeight(); + + // 以屏幕左上角为原点,设置x、y初始值,相对于gravity + mWmParams.x = 0; + mWmParams.y = mScreenHeight / 2; + + // 设置悬浮窗口长宽数据 + mWmParams.width = LayoutParams.WRAP_CONTENT; + mWmParams.height = LayoutParams.WRAP_CONTENT; + addView(createView(mContext)); + mWindowManager.addView(this, mWmParams); + + hide(); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + // 更新浮动窗口位置参数 靠边 + DisplayMetrics dm = new DisplayMetrics(); + // 获取屏幕信息 + mWindowManager.getDefaultDisplay().getMetrics(dm); + mScreenWidth = dm.widthPixels; + mScreenHeight = dm.heightPixels; + int oldX = mWmParams.x; + int oldY = mWmParams.y; + switch (newConfig.orientation) { + case Configuration.ORIENTATION_LANDSCAPE://横屏 + if ( mIsRight ) { + mWmParams.x = mScreenWidth; + mWmParams.y = oldY; + } else { + mWmParams.x = oldX; + mWmParams.y = oldY; + } + break; + case Configuration.ORIENTATION_PORTRAIT://竖屏 + if ( mIsRight ) { + mWmParams.x = mScreenWidth; + mWmParams.y = oldY; + } else { + mWmParams.x = oldX; + mWmParams.y = oldY; + } + break; + } + mWindowManager.updateViewLayout(this, mWmParams); + } + + /** + * 创建Float view + * @param context + * @return + */ + private View createView(final Context context) { + LayoutInflater inflater = LayoutInflater.from(context); + // 从布局文件获取浮动窗口视图 + mRootFloatView = inflater.inflate(ResourceUtils.getLayoutId(context, "pj_widget_float_view"), null); + mFlFloatLogo = (FrameLayout) mRootFloatView.findViewById(ResourceUtils.getId(context, "pj_float_view")); + + mIvFloatLogo = (ImageView) mRootFloatView.findViewById(ResourceUtils.getId(context, + "pj_float_view_icon_imageView")); + mIvFloatLoader = (ImageView) mRootFloatView.findViewById(ResourceUtils.getId( + context, "pj_float_view_icon_notify")); + mLlFloatMenu = (LinearLayout) mRootFloatView.findViewById(ResourceUtils.getId( + context, "ll_menu")); + mTvAccount = (TextView) mRootFloatView.findViewById(ResourceUtils.getId( + context, "tv_account")); + mTvAccount.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + mLlFloatMenu.setVisibility(View.GONE); + openUserCenter(); + } + }); + mTvFeedback = (TextView) mRootFloatView.findViewById(ResourceUtils.getId( + context, "tv_feedback")); + mTvFeedback.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + openFeedback(); + mLlFloatMenu.setVisibility(View.GONE); + } + }); + mRootFloatView.setOnTouchListener(this); + mRootFloatView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if ( !mDraging ) { + if (mLlFloatMenu.getVisibility() == View.VISIBLE) { + mLlFloatMenu.setVisibility(View.GONE); + } else { + mLlFloatMenu.setVisibility(View.VISIBLE); + } + } + } + }); + mRootFloatView.measure(MeasureSpec.makeMeasureSpec(0, + MeasureSpec.UNSPECIFIED), MeasureSpec + .makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + + + return mRootFloatView; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + removeTimerTask(); + // 获取相对屏幕的坐标,即以屏幕左上角为原点 + int x = (int) event.getRawX(); + int y = (int) event.getRawY(); + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mTouchStartX = event.getX(); + mTouchStartY = event.getY(); + mIvFloatLogo.setImageResource(ResourceUtils.getDrawableId( + mContext, "pj_image_float_logo")); + mWmParams.alpha = 1f; + mWindowManager.updateViewLayout(FloatView.this, mWmParams); + break; + case MotionEvent.ACTION_MOVE: + float mMoveStartX = event.getX(); + float mMoveStartY = event.getY(); + // 如果移动量大于3才移动 + if (Math.abs(mTouchStartX - mMoveStartX) > 3 + && Math.abs(mTouchStartY - mMoveStartY) > 3) { + // 更新浮动窗口位置参数 + mWmParams.x = (int) (x - mTouchStartX); + mWmParams.y = (int) (y - mTouchStartY); + mWindowManager.updateViewLayout(this, mWmParams); + mLlFloatMenu.setVisibility(View.GONE); + return false; + } + mDraging = true; + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + + if (mWmParams.x >= mScreenWidth / 2) { + mWmParams.x = mScreenWidth; + mIsRight = true; + } else if (mWmParams.x < mScreenWidth / 2) { + mIsRight = false; + mWmParams.x = 0; + } + mIvFloatLogo.setImageResource(ResourceUtils.getDrawableId( + mContext, "pj_image_float_logo")); + refreshFloatMenu(mIsRight); + timerForHide(); + mWindowManager.updateViewLayout(this, mWmParams); + // 初始化 + mTouchStartX = mTouchStartY = 0; + + mDraging = false; + break; + } + return false; + } + + private void removeTimerTask() { + if (mTimer != null) { + mTimer.cancel(); + mTimer = null; + } + if (mTimerTask != null) { + mTimerTask.cancel(); + mTimerTask = null; + } + } + + private void removeFloatView() { + try { + if (mRootFloatView != null) { + mWindowManager.removeView(mRootFloatView); + } + } catch (Exception ex) { + } + } + + /** + * 隐藏悬浮窗 + */ + public void hide() { + if (mRootFloatView != null) { + mRootFloatView.setVisibility(View.GONE); + removeTimerTask(); + } + } + + /** + * 显示悬浮窗 + */ + public void show() { + if (mRootFloatView != null) { + mIvFloatLogo.setImageResource(ResourceUtils.getDrawableId( + mContext, "pj_image_float_logo")); + mWmParams.alpha = 1f; + mWindowManager.updateViewLayout(FloatView.this, mWmParams); + mRootFloatView.setVisibility(View.VISIBLE); + timerForHide(); + if (mIvFloatLoader.getVisibility() == View.VISIBLE) { + Animation rotaAnimation = AnimationUtils.loadAnimation(mContext, + ResourceUtils.getAnimId(mContext, "pj_loading_anim")); + rotaAnimation.setInterpolator(new LinearInterpolator()); + mIvFloatLoader.startAnimation(rotaAnimation); + } + } + } + + /** + * 刷新float view menu + * @param right + */ + private void refreshFloatMenu(boolean right) { + if (right) { + LayoutParams paramsFloatImage = (LayoutParams) mIvFloatLogo.getLayoutParams(); + paramsFloatImage.gravity = Gravity.RIGHT; + mIvFloatLogo.setLayoutParams(paramsFloatImage); + LayoutParams paramsFlFloat = (LayoutParams) mFlFloatLogo.getLayoutParams(); + paramsFlFloat.gravity = Gravity.RIGHT; + mFlFloatLogo.setLayoutParams(paramsFlFloat); + + int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, mContext.getResources().getDisplayMetrics()); + int padding52 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 52, mContext.getResources().getDisplayMetrics()); + LinearLayout.LayoutParams paramsMenuAccount = (LinearLayout.LayoutParams) mTvAccount.getLayoutParams(); + paramsMenuAccount.rightMargin = padding; + paramsMenuAccount.leftMargin = padding; + mTvAccount.setLayoutParams(paramsMenuAccount); + + LinearLayout.LayoutParams paramsMenuFb = (LinearLayout.LayoutParams) mTvFeedback.getLayoutParams(); + paramsMenuFb.rightMargin = padding52; + paramsMenuFb.leftMargin = padding; + mTvFeedback.setLayoutParams(paramsMenuFb); + } else { + LayoutParams params = (LayoutParams) mIvFloatLogo.getLayoutParams(); + params.setMargins(0, 0, 0, 0); + params.gravity = Gravity.LEFT; + mIvFloatLogo.setLayoutParams(params); + LayoutParams paramsFlFloat = (LayoutParams) mFlFloatLogo.getLayoutParams(); + paramsFlFloat.gravity = Gravity.LEFT; + mFlFloatLogo.setLayoutParams(paramsFlFloat); + + int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, mContext.getResources().getDisplayMetrics()); + int padding52 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 52, mContext.getResources().getDisplayMetrics()); + + LinearLayout.LayoutParams paramsMenuAccount = (LinearLayout.LayoutParams) mTvAccount.getLayoutParams(); + paramsMenuAccount.rightMargin = padding; + paramsMenuAccount.leftMargin = padding52; + mTvAccount.setLayoutParams(paramsMenuAccount); + + LinearLayout.LayoutParams paramsMenuFb = (LinearLayout.LayoutParams) mTvFeedback.getLayoutParams(); + paramsMenuFb.rightMargin = padding; + paramsMenuFb.leftMargin = padding; + mTvFeedback.setLayoutParams(paramsMenuFb); + } + } + + /** + * 定时隐藏float view + */ + private void timerForHide() { + mCanHide = true; + mTimer = new Timer(); + + //结束任务 + if (mTimerTask != null) { + try { + mTimerTask.cancel(); + mTimerTask = null; + } catch (Exception e){} + + } + mTimerTask = new TimerTask() { + @Override + public void run() { + Message message = mTimerHandler.obtainMessage(); + message.what = HANDLER_TYPE_TIMER; + mTimerHandler.sendMessage(message); + } + }; + if (mCanHide) { + mTimer.schedule(mTimerTask, 6000, 3000); + } + } + + /** + * 打开用户中心 + */ + private void openUserCenter() { + } + + /** + * 打开客服页面 + */ + private void openFeedback() { + } + + /** + * 是否Float view + */ + public void destroy() { + hide(); + removeFloatView(); + removeTimerTask(); + try { + mTimerHandler.removeMessages(1); + } catch (Exception e){} + } +} diff --git a/app/src/main/res/anim/pj_loading_anim.xml b/app/src/main/res/anim/pj_loading_anim.xml new file mode 100644 index 0000000..f3e5c16 --- /dev/null +++ b/app/src/main/res/anim/pj_loading_anim.xml @@ -0,0 +1,29 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/pj_anim_background.png b/app/src/main/res/drawable-hdpi/pj_anim_background.png new file mode 100644 index 0000000..6706b3c Binary files /dev/null and b/app/src/main/res/drawable-hdpi/pj_anim_background.png differ diff --git a/app/src/main/res/drawable-hdpi/pj_image_float_left.png b/app/src/main/res/drawable-hdpi/pj_image_float_left.png new file mode 100644 index 0000000..c2e492e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/pj_image_float_left.png differ diff --git a/app/src/main/res/drawable-hdpi/pj_image_float_logo.png b/app/src/main/res/drawable-hdpi/pj_image_float_logo.png new file mode 100644 index 0000000..fbe510f Binary files /dev/null and b/app/src/main/res/drawable-hdpi/pj_image_float_logo.png differ diff --git a/app/src/main/res/drawable-hdpi/pj_image_float_right.png b/app/src/main/res/drawable-hdpi/pj_image_float_right.png new file mode 100644 index 0000000..facdc1b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/pj_image_float_right.png differ diff --git a/app/src/main/res/drawable-xxhdpi/pj_menu_account.png b/app/src/main/res/drawable-xxhdpi/pj_menu_account.png new file mode 100644 index 0000000..2155f04 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/pj_menu_account.png differ diff --git a/app/src/main/res/drawable-xxhdpi/pj_menu_fb.png b/app/src/main/res/drawable-xxhdpi/pj_menu_fb.png new file mode 100644 index 0000000..3da666f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/pj_menu_fb.png differ diff --git a/app/src/main/res/drawable/pj_menu_bg.xml b/app/src/main/res/drawable/pj_menu_bg.xml new file mode 100644 index 0000000..69674ed --- /dev/null +++ b/app/src/main/res/drawable/pj_menu_bg.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..bcc437b --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..beacad9 --- /dev/null +++ b/app/src/main/res/layout/content_main.xml @@ -0,0 +1,48 @@ + + + + + + +