English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

تحقيق إطلاق الذاكرة في الأندرويد مثل كرة تسارع 360

يوجد الآن العديد من تطبيقات الشاشة العائمة على الهاتف، بالنسبة للمستخدم، أكثر تطبيقات الشاشة العائمة شيوعًا هي وحدة التحكم الصغيرة العائمة لتطبيقات الأمان مثل 360 Guard، عند تشغيل الشاشة العائمة، يظهر كرة صغيرة، يمكن سحب الكرة، عند الضغط على الكرة، يظهر وحدة التحكم الكبيرة يمكن تنفيذ عمليات إضافية مثل إطلاق ذاكرة الهاتف وما إلى ذلك. لذا، استفدت من فيديو موكو، وتم إعادة تحقيق كرة تسريع 360، تم إضافة ميزة إطلاق ذاكرة الهاتف عند الضغط على الكرة.

بما أن الهاتف فقط له شاشة صورة الشاشة: كما يظهر في الصورة بعد التنفيذ: عند الضغط على زر التشغيل، يظهر كرة الشاشة العائمة التي تظهر نسبة الذاكرة المتاحة للهاتف؛ عند سحب الكرة، تتحول الكرة إلى أيقونة Android؛ عند إطلاق الكرة، تثبت الكرة على جانبي الشاشة؛ عند الضغط على الكرة، يظهر وحدة التحكم الكبيرة في أسفل الهاتف، عند الضغط على الكرة في الداخل، يتم إطلاق ذاكرة الهاتف؛ عند الضغط على أي منطقة أخرى من شاشة الهاتف، يختفي وحدة التحكم الكبيرة ويظهر الكرة مرة أخرى.

النتيجة كما يلي:

الخطوات المهمة التالية للاحترام:

1. تحقيق FloatCircleView (تعريف view)

عملية تحقيق FloatCircleView هي عملية تعريف view المخصص. 1. تعريف خصائص View 2. الحصول على الخصائص المخصصة في طريقة بناء View 3. إعادة كتابة onMesure 4. إعادة كتابة onDraw. لم نتعرف على خصائص أخرى لذا قمنا بتقليل العديد من الخطوات.

كل التعديلات الاولية، إعداد الشعار الذي سيتم عرضه على الكرة المتحركة، وقد تم حساب كافة الذاكرة.
 

 public int width=100;
  public int heigth=100;
  private Paint circlePaint;//画圆
  private Paint textPaint; //画字
  private float availMemory; //已用内存
  private float totalMemory; //总内存
  private String text;  //显示的已用内存百分比
  private boolean isDraging=false; //是否在拖动状态。
  private Bitmap src;
  private Bitmap scaledBitmap; //缩放后的图片。
 /**
   * 初始化画笔以及计算可用内存,总内存,和可用内存百分比。
   */
  public void initPatints() {
    circlePaint = new Paint();
    circlePaint.setColor(Color.CYAN);
    circlePaint.setAntiAlias(true);
    textPaint = new Paint();
    textPaint.setColor(Color.WHITE);
    textPaint.setTextSize(25);
    textPaint.setFakeBoldText(true);
    textPaint.setAntiAlias(true);
    //设置图片
    src = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
    //缩放后的图片(将图标设置的和悬浮小球一样大小。)
    scaledBitmap = Bitmap.createScaledBitmap(src, width, heigth, true);
    //计算已用内存,总内存,已用内存百分比,
    availMemory= (float) getAvailMemory(getContext());
    totalMemory= (float) getTotalMemory(getContext());
    text=(int)((availMemory/totalMemory)*100)+"%";
  }

onMeasure(); وهو كتابة العرض والارتفاع الثابتة، من خلال传入 setMeasuredDimension(width, heigth);.
onDraw(); رسم الكرة الصغيرة المتحركة. يتم تعريف متغير boolean لتقييم ما إذا كانت الكرة الصغيرة في حالة السحب أم لا، إذا كانت في حالة السحب، يتم رسم شعار android في هذا الموضع، وإذا لم تكن في حالة السحب، يتم رسم الكرة الصغيرة. ليس هناك صعوبة في رسم الكرة الصغيرة، ولكن النقطة الرئيسية هي رسم النص. يمكن للصورتين التالية زيادة فهمك لرسم النصوص.

1. مقياس x عند رسم النص (1.textPaint.measureText(text); للحصول على عرض الأحرف. 2. نصف عرض الكرة الصغيرة - نصف عرض الأحرف.)
2. مقياس y عند رسم النص (1.Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); للحصول على مقياس الأحرف. 2. (fontMetrics.ascent + fontMetrics.descent) / 2 للحصول على طول الأحرف. 3. نصف طول الكرة الصغيرة - نصف طول الأحرف.)

رسم رسمة يجعل الأمر واضحًا:

/**
   * رسم الكرة الصغيرة والنصوص. إذا كانت الكرة الصغيرة في حالة السحب، يتم عرض شعار android، وإذا لم تكن في حالة السحب، يتم عرض الكرة الصغيرة.
   * @param canvas
   */
  @Override
  protected void onDraw(Canvas canvas) {
    if (isDraging){
      canvas.drawBitmap(scaledBitmap, 0, 0, null);
    }
      //1. رسم دائرة
      canvas.drawCircle(width / 2, heigth / 2, width / 2, circlePaint);
      //2. رسم النص
      float textwidth = textPaint.measureText(text);//عرض النص
      float x = width / 2 - textwidth / 2;
      Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
      float dy = -(fontMetrics.ascent + fontMetrics.descent) / 2;
      float y = heigth / 2 + dy;
      canvas.drawText(text, x, y, textPaint);
    }
  }

طريقة الحصول على ذاكرة الجهاز المستخدمة والذاكرة الإجمالية:
   

public long getAvailMemory(Context context)
  {
    // الحصول على حجم الذاكرة المتاحة في android حاليًا
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
    am.getMemoryInfo(mi);
    //mi.availMem; ذاكرة النظام المتاحة حاليًا
    //return Formatter.formatFileSize(context, mi.availMem); // تعديل حجم الذاكرة المكتسبة
    return mi.availMem/(1024*1024);
  }
  public long getTotalMemory(Context context)
  {
    String str1 = "/proc/meminfo"; // ملف معلومات ذاكرة النظام
    String str2;
    String[] arrayOfString;
    long initial_memory = 0;
    try
    {
      FileReader localFileReader = new FileReader(str1);
      BufferedReader localBufferedReader = new BufferedReader;
          localFileReader, 8192);
      str2 = localBufferedReader.readLine(); // قراءة السطر الأول من meminfo، حجم ذاكرة النظام الإجمالية
      arrayOfString = str2.split("\\s+");
      for (String num : arrayOfString) {
        Log.i(str2, num + "\t");
      }
      initial_memory = Integer.valueOf(arrayOfString[1]).intValue() * 1024; // تحصل على ذاكرة النظام الإجمالية، وحدة هي KB، ضعف 1024 لتحويلها إلى Byte
      localBufferedReader.close();
    catch (IOException e) {
    }
    //return Formatter.formatFileSize(context, initial_memory);// Byte转换为KB或者MB,内存大小规格化
    return initial_memory/(1024*1024);
  }

2.创建WindowManager窗体管理类,管理悬浮小球和底部大窗体。

WindowManager类。用来管理整个悬浮小球和手机底部大窗体的显示和隐藏。

必须在Manifest文件中增加<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />权限。

通过 WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);获取窗体管理类;

利用wm.addView(view, params);将view增加到窗体中。

利用wm.remove(view,params);将view从窗体中移除。

利用wm.updateViewLayout(view,params);来更新view.

WindowManager.LayoutParams用来设置view的各种属性。

1.创建FloatViewManager实例。

//单例模式创建
 public static FloatViewManager getInstance(Context context){
    if (inStance==null){
      synchronized(FloatViewManager.class){
        if (inStance==null){
          inStance=new FloatViewManager(context);
        }
      }
    }
    return inStance;
  }

2.展示悬浮小球和展示底部窗体的方法。(展示窗体的方法同展示悬浮小球类似。)

/**
   * 展示浮窗
   */
  public void showFloatCircleView() {
  //参数设置
    if (params==null){
      params = new WindowManager.LayoutParams();
      //宽高
      params.width=circleView.width;
      params.height=circleView.heigth;
      //对齐方式
      params.gravity= Gravity.TOP|Gravity.LEFT;
      // تحديد التوجيه
      params.x=0;
      params.y=0;
      // نوع
      params.type=WindowManager.LayoutParams.TYPE_TOAST;
      // تعيين خصائص النافذة.
      params.flags= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
      // تنسيق البكسل
      params.format= PixelFormat.RGBA_8888;
    }
    // يتم إضافة الكرة إلى النافذة.
    wm.addView(circleView, params);
  }
 public void showFloatCircleView() {
 ......
 }

 3. عند بدء التطبيق، يتم إنشاء الكرة المعلقة أولاً، يمكن سحب الكرة، عند النقر على الكرة، يظهر واجهة الهاتف في الأسفل (FloatMenuView)، وتختفي الكرة. لذا، يجب على الكرة (circleView) أن يتم تخصيصها لـ setOnTouchListener وsetOnClickListener لمراقبة الأحداث.

تحليل توزيع أحداث الكرة؛ بالنسبة للكرة:

عند ACTION_DOWN، يتم تسجيل downX، downY للكرة، وكذلك startX، startY،

عند ACTION_MOVE، يتم تعيين dragState لـ circleView على true، وتسجيل moveX، moveY للكرة، حساب مسافة تحرك الكرة (dx، dy)، ثم تحديث موقع الكرة باستخدام wm.updateViewLayout(circleView, params). في النهاية، يتم تخصيص أحدث إحداثيات التحرك لـ startX، startY.

عند ACTION_UP، يتم تعيين dragState لـ circleView على false، وتسجيل إحداثيات الرفع، upx، بناءً على upx وعرض الشاشة للهاتف / 2، يتم الحكم على ما إذا كانت الكرة ملصقة على الجانب الأيسر للشاشة أم الجانب الأيمن. بعد ذلك، هناك خطأ في تمرير الكرة. عند تمرير الكرة لمسافة أقل من 10 بكسل، يمكن إطلاق حدث النقر على الكرة. (يتمتع حدث اللمس لمس الكرة بأولوية أمام حدث النقر على الكرة، عند عودة حدث اللمس إلى true، يتم استهلاك هذا الحدث وعدم انتقال الحدث إلى أسفل. عند عودة حدث اللمس إلى false، يستمر الحدث في التمرير، مما يتسبب في إطلاق حدث النقر على الكرة.)

حدث النقر على الكرة: عند النقر على الكرة، تختفي الكرة المعلقة، يظهر واجهة الهاتف في الأسفل. ويتم تعيين حركة انتقال عند ظهور واجهة الأسفل.

//لإعداد circleView لمستمع اللمس.
  private View.OnTouchListener circleViewOnTouchListener=new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
      switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
        //最后按下时的坐标,根据ACTION_MOVE理解。
          startX = event.getRawX();
          startY = event.getRawY();
          //按下时的坐标。
          downX = event.getRawX();
          downY = event.getRawY();
          break;
        case MotionEvent.ACTION_MOVE:
          circleView.setDrageState(true);
           moveX = event.getRawX();
           moveY=event.getRawY();
          float dx = moveX -startX;
          float dy=moveY-startY;
          params.x+=dx;
          params.y+=dy;
          wm.updateViewLayout(circleView,params);
          startX= moveX;
          startY=moveY;
          break;
        case MotionEvent.ACTION_UP:
          float upx=event.getRawX();
          if (upx>getScreenWidth()/2){
            params.x=getScreenWidth()-circleView.width;
          }
            params.x=0;
          }
          circleView.setDrageState(false);
          wm.updateViewLayout(circleView,params);
          if (Math.abs(moveX-downX)>10){
            return true;
          }
            return false;
          }
        default:
          break;
      }
      return false;
    }
  };
circleView.setOnTouchListener(circleViewOnTouchListener);
    circleView.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        //Toast.makeText(, "onclick", Toast.LENGTH_SHORT).show();
        //隱藏circleView,顯示菜单栏。
        wm.removeView(circleView);
        showFloatMenuView();
        floatMenuView.startAnimation();
      }
    });

3.MyProgreeView(手机底部窗体中小球的实现)。

1.初始化画笔,对view进行手势监听。监听单击和双击事件。(必须设置view是可以点击的)

private void initPaint() {
    //画圆画笔
    circlepaint = new Paint();
    circlepaint.setColor(Color.argb(0xff, 0x3a, 0x8c, 0x6c));
    circlepaint.setAntiAlias(true);
    //画进度条画笔
    progerssPaint = new Paint();
    progerssPaint.setAntiAlias(true);
    progerssPaint.setColor(Color.argb(0xff, 0x4e, 0xcc, 0x66));
    progerssPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//绘制重叠部分
    //画进度画笔
    textPaint = new Paint();
    textPaint.setAntiAlias(true);
    textPaint.setColor(Color.WHITE);
    textPaint.setTextSize(25);
    //画布
    bitmap = Bitmap.createBitmap(width, heigth, Bitmap.Config.ARGB_8888);
    bitmapCanvas = new Canvas(bitmap);
    //手势监听。
    gestureDetector = new GestureDetector(new MyGertureDetectorListener());
    setOnTouchListener(new OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
      }
    });
    //设置 view 可点击。
    setClickable(true);
  }
  class MyGertureDetectorListener extends GestureDetector.SimpleOnGestureListener{
    @Override
    public boolean onDoubleTap(MotionEvent e) {
    ......
     //双击事件的逻辑
      return super.onDoubleTap(e);
    }
    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
    ......
     //单击事件的逻辑
      return super.onSingleTapConfirmed(e);
    }
  }

2. 通过 handler 交互更新单击和双击事件的状态。单击时,使用贝塞尔曲线实现波纹荡漾效果。双击时,波纹不断下降,进行内存释放,最后显示释放内存后的已用内存百分比。handler 发送周期消息,使单击事件和双击事件的小球不断进行重绘。(重绘将在下一小节讲解)。

//事件 单击 发送周期 handler.
private void startSingleTapAnimation() {
    handler.postDelayed(singleTapRunnable,200); 
  }
  private SingleTapRunnable singleTapRunnable=new SingleTapRunnable();
  class SingleTapRunnable implements Runnable{
    @Override
    public void run() {
      count--;
      if (count>=0) {
        invalidate();// إعادة الرسم بشكل مستمر.
        handler.postDelayed(singleTapRunnable,200);
      }
        handler.removeCallbacks(singleTapRunnable);
        count=50;
      }
    }
  }
  //事件 双击 发送周期 handler。
  private void startDoubleTapAnimation() {
    handler.postDelayed(runnbale,50);
  }
  private DoubleTapRunnable runnbale=new DoubleTapRunnable();
  class DoubleTapRunnable implements Runnable{
    @Override
    public void run() {
      num--;
      if (num>=0){
        invalidate();// إعادة الرسم بشكل مستمر.
        handler.postDelayed(runnbale,50);
      }
        handler.removeCallbacks(runnbale);
        // إطلاق الذاكرة.
       killprocess();
       // حساب نسبة الذاكرة المستخدمة بعد الإفراج.
        num=(int)(((float)currentProgress/max)*100);
      }
    }
  }

3. إعادة رسم بعد النقر المزدوج والنقر الواحد.

أولاً، رسم الكرة، ورسم مسار الاهتزاز.
// رسم الكرة
    bitmapCanvas.drawCircle(width / 2, heigth / 2, width / 2, circlepaint);
    // رسم مسار الاهتزاز بناءً على path. قبل كل رسم، قم بإعادة تعيين path السابق.
    path.reset();
    float y =(1-(float)num/100)*heigth;
    path.moveTo(width, y);
    path.lineTo(width, heigth);
    path.lineTo(0, heigth);
    path.lineTo(0, y);

استخدم خطوط البيسيير لرسم مسار الاهتزاز.

Android-خطوط البيسيير

تطبيق خطوط البيسيير في Android

هنا يوجد شرح مفصل عن خطوط البيسيير. في الواقع، لا تحتاج إلى فهم عميق. فقط تحتاج إلى معرفة أنك تستطيع استخدامها لتحقيق تأثير موجة الماء (هناك العديد من الاستخدامات لخطوط البيسيير، ويمكن استخدامها أيضًا لتحقيق تأثير التدوير في الكتابة.). الرئيسية هي استخدام path.rQuadTo(x1, y1, x2, y2); لتحديد النقطة النهائية (x2،y2)، ونقطة التحكم المساعدة (x1،y1) لخط البيسيير. لذلك، من خلال تغيير موقع y1 بشكل مستمر، يمكننا رسم تأثير موجة الماء.

أولاً، قم بتحديد ما إذا كان النقر مزدوجًا:

إذا كان النقر مزدوجًا: قم بتعيين متغير d، من خلال تغيير قيمة d بشكل مستمر (تغيير قيمة d يتم بفضل num، الذي ينخفض باستمرار في handler. num--;)، لرسم خطوط البيسيير. لتحقيق تأثير انخفاض موجة الماء.

إذا كان النقر مزدوجًا: قم بتعيين قيمة count، من خلال تغيير قيمة count بشكل مستمر (تغيير قيمة count يتم في handler. count--;)، أولاً قم بتحديد ما إذا كان count قابلًا للقسمة على 2، قم برسم هذين الخطين البيسيير بشكل متناوب. (هذان الخطين البيسيير متعاكسان)، مما يؤدي إلى تحقيق تأثير اهتزاز الماء.
يستخدم حلقة for لتحقيق عدد الموجات، يمكن للـ path.rQuadTo(); تحقيق موجة واحدة فقط. يمكنك التحقق من ذلك بنفسك.

 if (!isSingleTap){
    float d=(1-(float)num/(100/2))*10;
      for (int i=0;i<3;i++){
      path.rQuadTo(10,-d,20,0);
      path.rQuadTo(10,d,20,0);
       }
    }
      float d=(float)count/50*10;
      if (count%2==0){
        for (int i=0;i<=3;i++){
          path.rQuadTo(10,-d,30,0);
          path.rQuadTo(10,d,30,0);
        }
      }
        for (int i=0;i<=3;i++){
          path.rQuadTo(10,d,30,0);
          path.rQuadTo(10,-d,30,0);
        }
      }
    }

آخر شيء هو إطلاق الذاكرة. تذكر أن تضيف صلاحية <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/> في ملف Manifest.
 

public void killprocess(){
    ActivityManager activityManger=(ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> list=activityManger.getRunningAppProcesses();
    if(list!=null)
      for(int i=0;i<list.size();i++)
      {
        ActivityManager.RunningAppProcessInfo apinfo=list.get(i);
        String[] pkgList=apinfo.pkgList;
 if(apinfo.importance>ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE)
        {
          // Process.killProcess(apinfo.pid);
          for(int j=0;j<pkgList.length;j++) {
            boolean flag=pkgList[j].contains("com.example.yyh.animation360");//هنا يجب التحقق من أن التطبيق الحالي، وإلا قد ينتهي التطبيق الحالي أيضًا.
            if(!flag){
            activityManger.killBackgroundProcesses(pkgList[j]);
          }
          }
        }
      }

4.تحقيق FloatMenuView.

1.إنشاء ملف float_menuview.xml؛ يحتوي على ImageView + TextView + MyProgreeView مخصص.
النافذة السفلية يجب أن تكون قابلة للنقر. android:clickable="true";

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#33000000"
  >
  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:background="#F02F3942"
    android:layout_alignParentBottom="true"
    android:id="@+id/ll"
    android:clickable="true"
    >
    <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:orientation="horizontal"
      >
      <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:src="@mipmap/ic_launcher"
        android:layout_gravity="center_vertical"
        />
      <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="15sp"
        android:textColor="#c93944"
        android:text="360加速球"
        android:layout_gravity="center_vertical"
        />
    </LinearLayout>
    <com.example.yyh.animation360.view.MyProgreeView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center_horizontal"
      android:layout_marginTop="10dp"
      />
  </LinearLayout>
</RelativeLayout>

2. إضافة FloatMenuView بناءً على الشروط، باستخدام (wm.addView(view, params); لضبط view في الواجهة.

باستخدام wm.remove(view,params); لإزالة view من الواجهة، يتم عرض وتخفيض واجهة المستخدم السفلية.
استخدام TranslateAnimation لضبط تأثير التحرك للواجهة السفلية عند الدخول. TranslateAnimation(int fromXType,float fromXValue,int toXType,float toXValue,int fromYType,float fromYValue,int toYType,float toYValue)
int fromXType: هناك خيارات ثلاثة للإشارة إلى قيمة البداية في اتجاه المحور X (1.Animation.ABSOLUTE: قيمة الإحداثيات المحددة، تشير إلى وحدة بكسل الشاشة المطلقة.

2.Animation.RELATIVE_TO_SELF: قيمة الإحداثيات الخاصة بك. 3.Animation.RELATIVE_TO_PARENT: قيمة الإحداثيات الخاصة بوالد الكونترول.
float fromXValue: هو parameter الثاني هو قيمة البداية للنوع السابق للparameter (مثلاً، إذا تم تعيين parameter الأول إلى Animation.RELATIVE_TO_SELF، فإن parameter الثاني هو 0.1f يعني ضرب قيمة الإحداثيات الخاصة بك في 0.1);

int toXType: هناك خيارات ثلاثة للإشارة إلى قيمة النهاية في اتجاه المحور X نفسها مثل parameter الأول.

float toValue: هو parameter الرابع هو قيمة البداية للنوع السابق للparameter.

النسبة نفسها لمنحنى Y. نقطة البداية + نقطة النهاية;(كل parameter يليه parameter التالي هو قيمة البداية للparameter السابق。)

وإذا تم تعيين OnTouchListener لهذا view، يجب أن يعود الحدث OnTouch إلى false في النهاية، مما يعني أن هذا الحدث يجب أن يتم نقلها إلى أسفل. وبالتالي، يتم إخفاء واجهة المستخدم السفلية للهاتف عند النقر على منطقة أخرى من الهاتف، وعرض الكرة المعلقة، لا يحدث أي شيء عند النقر على واجهة المستخدم السفلية، وتفعيل أحداث النقر الواحد والنقر المزدوج عند النقر على الكرة في واجهة المستخدم السفلية.

 View view =View.inflate(getContext(), R.layout.float_menuview,null);
    LinearLayout linearLayout= (LinearLayout) view.findViewById(R.id.ll);
    translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,0,Animation.RELATIVE_TO_SELF,1.0f,Animation.RELATIVE_TO_SELF,0);
    translateAnimation.setDuration(500);
    translateAnimation.setFillAfter(true);
    linearLayout.setAnimation(translateAnimation);
    view.setOnTouchListener(new OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
        FloatViewManager manager=FloatViewManager.getInstance(getContext());
        manager.hideFloatMenuView();
        manager.showFloatCircleView();
        return false;
      }
    });
    addView(view);

5.MyFloatService

用来创建FloatVIewManager单例,管理悬浮小球+手机底部窗体的创建和移除。

public class MyFloatService extends Service {
  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    return null;
  }
  @Override
  public void onCreate() {
    //用来开启FloatViewManager
    FloatViewManager manager=FloatViewManager.getInstance(this);
    manager.showFloatCircleView();
    super.onCreate();
  }
}

6. تنفيذMainActivity

تعريف intent، افتح الخدمة (في الخدمة، قم بإنشاء نموذجWindowManager Singleton، لإدارة الكرات المعلقة وواجهة المستخدم السفلية للهاتف.)، أغلق هذاactivity.

public class MainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }
  public void startService(View view){
    Intent intent=new Intent(this, MyFloatService.class);
    startService(intent);
    finish();
  }
}

هذا هو نهاية محتوى هذا المقال، نأمل أن يكون قد ساعدكم في التعلم، ونأمل أيضًا أن تدعموا تدريب النطق.

البيان: محتوى هذا المقال تم جمعه من الإنترنت، يحق لصاحب الحقوق أن يكون مالكًا، يتم جمع المحتوى من قبل المستخدمين على الإنترنت بشكل تلقائي، هذا الموقع لا يملك حقوق الملكية، ولا يتم تعديل المحتوى بشكل يدوي، ولا يتحمل أي مسؤولية قانونية. إذا اكتشفت محتوى يشتبه في انتهاك حقوق النسخ، فلا تتردد في إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال #بـ @) لإبلاغنا، وقدم الأدلة ذات الصلة، إذا تم التحقق من ذلك، فإن هذا الموقع سيقوم بإزالة المحتوى المزعوم بسرعة.

أنت قد تعجبك