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

Android clone360 Ball de aceleração para liberar memória

现在手机上的悬浮窗应用越来越多,对用户来说,最常见的悬浮窗应用就是安全软件的悬浮小控件,拿360卫士来说,当开启悬浮窗时,它是一个小球,小球可以拖动,当点击小球出现大窗体控件,可以进行进一步的操作如:释放手机内存等等。于是借着慕课网的视频,仿着实现了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);
    //Definir a imagem
    src = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
    //Imagem escalonada (definir o tamanho do ícone e da esfera flutuante iguais).
    scaledBitmap = Bitmap.createScaledBitmap(src, width, height, true);
    //Calcular a memória usada, a memória total, a porcentagem de memória usada,
    availMemory= (float) getAvailMemory(getContext());
    totalMemory= (float) getTotalMemory(getContext());
    text=(int)((availMemory/totalMemory)*100)+"%";
  }

onMeasure(); é escrever as alturas e larguras fixas, através de setMeasuredDimension(width, height); passar.
onDraw(); realiza o desenho da esfera flutuante. Definir uma variável booleana para determinar se o estado atual é o estado de arrastar a esfera. Se for o estado de arrastar a esfera, desenhará o ícone do Android na posição, se não for o estado de arrastar, realizará o desenho da esfera. O desenho da esfera não é difícil, a chave é o desenho do texto. Abaixo2uma imagem pode aprofundar a compreensão sobre o desenho do texto.

1.Coordenada x ao desenhar o texto (1.textPaint.measureText(text); obter a largura da letra.2.Largura da esfera/2-Largura da letra/2).
2.Coordenada y ao desenhar o texto (1.Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); obter a classe de medição de atributos da fonte.2.(fontMetrics.ascent + fontMetrics.descent) / 2 Obter a altura da letra.3.Altura da esfera/2-Altura da fonte/2)

Desenhar uma imagem é fácil de entender:

/**
   * Desenhar a pequena esfera e o texto. Se a esfera estiver em estado de arrastar, mostrará o ícone do Android, se não estiver em estado de arrastar, mostrará a esfera.
   * @param canvas
   */
  @Override
  protected void onDraw(Canvas canvas) {
    if (isDraging){
      canvas.drawBitmap(scaledBitmap, 0, 0, null);
    }else {
      //1.Desenhar círculo
      canvas.drawCircle(width) / 2, heigth / 2, width / 2, circlePaint);
      //2.desenhar text
      float textwidth = textPaint.measureText(text);//largura do texto
      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);
    }
  }

método para obter a memória usada e total do telefone:
   

public long getAvailMemory(Context context)
  {
    // obter o tamanho da memória disponível atual do android
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
    am.getMemoryInfo(mi);
    //mi.availMem; memória disponível do sistema atual
    //return Formatter.formatFileSize(context, mi.availMem);// padronize o tamanho da memória obtida
    return mi.availMem/(1024*1024);
  }
  public long getTotalMemory(Context context)
  {
    String str1 = "/proc/meminfo";// arquivo de informações de memória do sistema
    String str2;
    String[] arrayOfString;
    long initial_memory = 0;
    try
    {
      FileReader localFileReader = new FileReader(str1);
      BufferedReader localBufferedReader = new BufferedReader(
          localFileReader, 8192);
      str2 = localBufferedReader.readLine();// Lê a primeira linha de meminfo, tamanho total da memória do sistema
      arrayOfString = str2.split("\\s+);
      for (String num : arrayOfString) {
        Log.i(str2, num + "\t");
      }
      initial_memory = Integer.valueOf(arrayOfString[1]).intValue() * 1024;// Obtém a memória total do sistema, em KB, multiplicada por1024Conversão para Byte
      localBufferedReader.close();
    }
    }
    //return Formatter.formatFileSize(context, initial_memory);// Conversão de Byte para KB ou MB, padronização do tamanho da memória
    return initial_memory/(1024*1024);
  }

2. cria uma classe de gerenciamento de janela WindowManager, gerencia o balão flutuante e a grande janela inferior.

Classe WindowManager. Usada para gerenciar a exibição e ocultação de todo o balão flutuante e a grande janela inferior do telefone.

Deve ser adicionado <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />Permissão。

Obtém a classe de gerenciamento de janela através de WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

Utiliza wm.addView(view, params) para adicionar o view à janela.

Utiliza wm.remove(view, params) para remover o view da janela.

Utiliza wm.updateViewLayout(view, params) para atualizar o view.

WindowManager.LayoutParams usado para configurar várias propriedades do view.

1. cria uma instância de FloatViewManager.

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

2. Métodos para exibir a bolinha flutuante e a janela inferior. (O método para exibir a janela é semelhante ao para exibir a bolinha flutuante.)

/**
   * Exibir janela flutuante
   */
  public void showFloatCircleView() {
  //Configuração de parâmetros
    if (params == null) {
      params = new WindowManager.LayoutParams();
      //Largura e altura
      params.width = circleView.width;
      params.height = circleView.heigth;
      //Alinhamento
      params.gravity = Gravity.TOP | Gravity.LEFT;
      //Deslocamento
      params.x=0;
      params.y = 0;
      //Tipo
      params.type = WindowManager.LayoutParams.TYPE_TOAST;
      //Configurar as propriedades da janela.
      params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
      //Formato de pixel
      params.format = PixelFormat.RGBA_8888;
    }
    //Adicionar a bolinha à janela.
    wm.addView(circleView, params);
  }
 public void showFloatCircleView() {
 ......
 }

 3. Quando o programa é iniciado, primeiro criar a bolinha flutuante, a bolinha pode ser arrastada, clicar na bolinha, a janela inferior do telefone mostra (FloatMenuView), a bolinha desaparece. Portanto, para a bolinha (circleView), deve-se configurar setOnTouchListener e setOnClickListener para eventos de escuta.

Analisar a distribuição de eventos do boliche; Para o boliche:

Quando ACTION_DOWN, registrar downX, downY do boliche, bem como startX, startY

Quando ACTION_MOVE, configurar o estado de arrastamento de circleView como verdadeiro, registrar moveX, moveY do boliche, calcular a distância de movimento do boliche (dx, dy), e então atualizar a posição do boliche com wm.updateViewLayout(circleView, params);. Por fim, atribuir as últimas coordenadas move ao startX, startY.

Quando ACTION_UP, configurar circleView para não arrastar como falso, registrar as coordenadas de levantamento, upx, com base em upx e a largura da tela do telefone/2,faça a verificação para determinar se a esfera está colada ao lado esquerdo ou direito da tela. A seguir estão os erros de arrastamento da esfera. Quando a distância de arrastamento da esfera for menor10pixels, pode acionar o evento de clique da esfera. (O evento de toque da esfera tem prioridade sobre o evento de clique da esfera. Quando o evento de toque retorna true, esse evento é consumido e não é mais transmitido para baixo. Quando o evento de toque retorna false, esse evento continua a ser transmitido para baixo, acionando assim o evento de clique da esfera.)

Evento de clique da esfera: clique na esfera, ocultar a esfera flutuante, aparecer a janela inferior do telefone e definir a animação de transição ao aparecer a janela inferior.

//Definir o listener de toque para circleView.
  private View.OnTouchListener circleViewOnTouchListener=new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
      switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
        //Últimas coordenadas ao pressionar, conforme ACTION_MOVE.
          startX = event.getRawX();
          startY = event.getRawY();
          //Coordenadas ao pressionar.
          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;
          }else {
            params.x=0;
          }
          circleView.setDrageState(false);
          wm.updateViewLayout(circleView,params);
          if (Math.abs(moveX-downX)>10) {
            return true;
          }else {
            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();
        //Hide circleView, show the menu bar.
        wm.removeView(circleView);
        showFloatMenuView();
        floatMenuView.startAnimation();
      }
    });

3.MyProgreeView (the implementation of the ball in the bottom window of the phone).

1.Initialize the paint, listen to the gestures of the view. Listen to single-click and double-click events. (The view must be set as clickable)

private void initPaint() {
    //drawCirclePaint
    circlePaint = new Paint();
    circlePaint.setColor(Color.argb(0xff, 0x3a, 0x8c, 0x6c));
    circlePaint.setAntiAlias(true);
    //progressBarPaint
    progressPaint = new Paint();
    progressPaint.setAntiAlias(true);
    progressPaint.setColor(Color.argb(0xff, 0x4e, 0xcc, 0x66));
    progressPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//drawOverlappingArea
    //progressPaint
    textPaint = new Paint();
    textPaint.setAntiAlias(true);
    textPaint.setColor(Color.WHITE);
    textPaint.setTextSize(25);
    //canvas
    bitmap = Bitmap.createBitmap(width, height, 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);
      }else {
        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);
      }else {
        handler.removeCallbacks(runnbale);
        //Liberar memória.
       killprocess();
       //Calcular a porcentagem de memória usada após o liberar.
        num=(int)(((float)currentProgress/max)*100);
      }
    }
  }

3. Redesenhar o evento de clique simples e o evento de clique duplo.

Primeiro é o desenho da pequena esfera, e o desenho do caminho das ondulações.
//Desenhar a pequena esfera
    bitmapCanvas.drawCircle(width / 2, heigth / 2, width / 2, circlepaint);
    //De acordo com o path, desenhar o caminho das ondulações. Antes de cada desenho, resetar o path anterior.
    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);

Em seguida, usar a curva de Bézier para desenhar o caminho das ondulações.

Android-Curva de Bézier

Aplicação da curva de Bézier no Android

Aqui há uma explicação detalhada sobre a curva de Bézier. Na verdade, não é necessário entender profundamente. Basta saber que pode ser usada para implementar o efeito das ondulações d'água (a curva de Bézier tem muitos usos, o efeito de capinha também pode ser implementado com ela.) Principalmente usando path.rQuadTo(x1,y1,x2,y2); Ponto final (x2, y2),ponto de controle auxiliar (x1, y1) curva de Bézier. Portanto, alterando constantemente y1posição, podemos desenhar o efeito das ondulações d'água.

Primeiro verificar se é um evento de clique duplo:

Se clicar duas vezes: definir uma variável d, alterando constantemente o valor de d (a alteração do valor de d é causada por num, que é constantemente diminuído no handler. num--;), para desenhar a curva de Bézier. Implementar o efeito de descida das ondulações d'água.

Se clicar: definir um valor de count, alterando constantemente o valor de count (a alteração do valor de count é implementada no handler. count--;), primeiro verificar se o count pode ser2整除,交替绘制这两条贝塞尔曲线。(这两条贝塞尔曲线正好相反),从而实现水波荡漾的效果。
(使用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);
       }
    }else {
      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);
        }
      }else {
        for (int i=0;i<=3;i++) {
          path.rQuadTo(10,d,30,0);
          path.rQuadTo(10,-d,30,0);
        }
      }
    }

No final, há métodos para liberar memória. Lembre-se de adicionar <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>Permissão。
 

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.animation"360");//这里要判断是否为当前应用,要不然也可能会结束当前应用。
            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="#F0"2F3942"
    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="#c"93944"
        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. Adicione FloatMenuView, de acordo com as condições, usando (wm.addView(view, params); para adicionar o view à janela.

Utilize o método wm.remove(view,params); para remover o view da janela, para exibir e ocultar a view da janela inferior.
TranslateAnimation é usada para definir o efeito de animação de entrada da janela inferior. TranslateAnimation(int fromXType,float fromXValue,int toXType,float toXValue,int fromYType,float fromYValue,int toYType,float toYValue)
int fromXType: os valores de referência do ponto de partida no eixo X são3Opções. (1.Animation.ABSOLUTE: valor de coordenada específico, referente a unidades de pixels de tela absolutos.)

2.Animation.RELATIVE_TO_SELF: o valor de coordenada em relação a si mesmo.)3.Animation.RELATIVE_TO_PARENT: o valor de coordenada em relação ao container pai.)
float fromXValue: o segundo parâmetro é o valor de início do tipo do primeiro parâmetro (por exemplo, se o primeiro parâmetro for definido como Animation.RELATIVE_TO_SELF, o segundo parâmetro será 0.1f, significa multiplicar o valor de coordenada próprio pelo 0.1);

int toXType: os valores de referência do ponto final no eixo X são3As opções são as mesmas que o primeiro parâmetro.

float toValue: o quarto parâmetro é o valor de início do tipo do terceiro parâmetro.

Os parâmetros na direção do eixo Y são idênticos. Ponto de partida+Ponto final; (o parâmetro seguinte de cada parâmetro é o valor de início do parâmetro anterior.)

E configure o OnTouchListener para este view, o evento OnTouch deve sempre retornar false, o que significa que o evento ainda precisa ser transmitido para baixo. Dessa forma, ao clicar em outras áreas do telefone, a janela inferior do telefone desaparece e a bola flutuante é exibida, ao clicar na janela inferior não há alteração, e ao clicar na bola na janela inferior, são acionados os eventos de clique simples e duplo.

 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

Usado para criar uma instância única de FloatVIewManager, gerenciar a bola flutuante+Criação e remoção da janela inferior do telefone.

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

6Implementação da .MainActivity

Defina um intent, inicie o serviço (crie uma instância singleton de WindowManager no serviço, gerencie a bolinha flutuante e a janela inferior do telefone), feche o activity atual.

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();
  }
}

Isso é tudo o que há no artigo, esperamos que ajude no seu aprendizado e que você apoie fortemente o tutorial de clamor.

Declaração: o conteúdo deste artigo é extraído da Internet, pertencente ao autor original, fornecido pelos usuários da Internet de forma voluntária e auto-publicada. Este site não possui direitos de propriedade, não foi editado manualmente e não assume responsabilidades legais relacionadas. Se você encontrar conteúdo suspeito de violação de direitos autorais, por favor, envie e-mail para: notice#oldtoolbag.com (ao enviar e-mail, substitua # por @) para denunciar, forneça provas relacionadas e, uma vez confirmado, o site deletará imediatamente o conteúdo suspeito de infração.

Você também pode gostar