亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

autojs模仿QQ長(zhǎng)按彈窗菜單實(shí)現(xiàn)示例詳解二

 更新時(shí)間:2023年01月26日 12:00:21   作者:牙叔教程  
這篇文章主要為大家介紹了autojs模仿QQ長(zhǎng)按彈窗菜單實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

上一節(jié)講了列表和長(zhǎng)按事件

autojs模仿QQ長(zhǎng)按彈窗菜單

彈窗菜單

由粗到細(xì), 自頂向下的寫代碼

我們現(xiàn)在要修改的文件是showMenuWindow.js

function showMenuWindow(view) {
  let popMenuWindow = ui.inflateXml(
    view.getContext(),
    `
    <column>
    <button id="btn1" text="btn1" />
    </column>
    `,
    null
  );
  let mPopWindow = new PopupWindow(popMenuWindow, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
  mPopWindow.setOutsideTouchable(true);
  mPopWindow.showAsDropDown(view);
}
module.exports = showMenuWindow;

我們先修改xml, QQ的彈窗由兩部分組成

  • 菜單列表
  • 箭頭

因此, xml如下

<column>
  <androidx.recyclerview.widget.RecyclerView id="recyclerView" padding="16" layout_width="match_parent" layout_height="match_parent">
  </androidx.recyclerview.widget.RecyclerView>
  <android.view.View id='arrow' ></android.view.View>
</column>

這給菜單我們用的也是recyclerView, 因此先設(shè)置他的adapter, 如果不會(huì)就看上一節(jié)課程;

function showMenuWindow(view) {
  let popMenuWindow = ui.inflateXml(
    ...
  );
  setPopMenuRecyclerViewAdapter(popMenuWindow.recyclerView, []);
  ...
}

設(shè)置Adapter的時(shí)候, 第一個(gè)參數(shù)我們是有的, 第二個(gè)參數(shù)是adapter要綁定的數(shù)據(jù), 現(xiàn)在沒(méi)有;

這給菜單數(shù)據(jù)應(yīng)該有哪些屬性呢?

  • 菜單顯示的文字
  • 菜單點(diǎn)后的回調(diào)函數(shù)

因此, 數(shù)據(jù)大概是這樣的

  menus: [
    {
      name: "復(fù)制",
      handle: () => {
        console.log("復(fù)制");
      },
    },
    {
      name: "分享",
      handle: () => {
        console.log("分享");
      },
    },
  ],

這種可配置的數(shù)據(jù), 我們把它放到config.js中.

數(shù)據(jù)有了, 接下來(lái)我們進(jìn)入setPopMenuRecyclerViewAdapter方法內(nèi)部,

提醒一下, 我是復(fù)制黏貼的上一節(jié)課的setAdapter方法, 因此設(shè)置recyclerview的方法大差不差.

setPopMenuRecyclerViewAdapter.js

let definedClass = false;
const PopMenuRecyclerViewViewHolder = require("./PopMenuRecyclerViewViewHolder");
const PopMenuRecyclerViewAdapter = require("./PopMenuRecyclerViewAdapter");
const showMenuWindow = require("../showMenuWindow.js");
module.exports = async function (recyclerView, items) {
  if (!definedClass) {
    await $java.defineClass(PopMenuRecyclerViewViewHolder);
    await $java.defineClass(PopMenuRecyclerViewAdapter);
    definedClass = true;
  }
  var adapter = new PopMenuRecyclerViewAdapter(items);
  adapter.setLongClick(showMenuWindow);
  recyclerView.setAdapter(adapter);
};

基本上就是復(fù)制黏貼, 修改一下類名即可

PopMenuRecyclerViewAdapter.js中, 修改一下holderXml即可

PopMenuRecyclerViewViewHolder.js, bind需要修改

bind(item) {
  this.itemView.attr("text", item);
  this.item = item;
}

除了設(shè)置adapter, 菜單彈框還需要設(shè)置layoutManager, 這樣我們可以控制水平方向上菜單的數(shù)量

const layoutManager = new androidx.recyclerview.widget.GridLayoutManager(this, 5);
grid.setLayoutManager(layoutManager);

先設(shè)置layoutManager, 再設(shè)置adapter

PopMenuRecyclerViewViewHolder.js, 需要修改一下bind方法, 他的item是對(duì)象, 文本是item.name

bind(item) {
  this.itemView.attr("text", item.name);
  this.item = item;
}

運(yùn)行代碼, 看看效果

菜單出來(lái)了, 接著寫箭頭, 菜單的xml是

<column>
  <androidx.recyclerview.widget.RecyclerView id="recyclerView" padding="16" layout_width="match_parent" layout_height="match_parent">
  </androidx.recyclerview.widget.RecyclerView>
  <android.view.View id='arrow' ></android.view.View>
</column>

下面那個(gè)View就是我們放箭頭的地方

箭頭

箭頭可能指向上方, 也可能指向下方, 我們通過(guò)設(shè)置View的前景, 來(lái)展示箭頭

arrowView.setForeground(drawable);

這里我們要寫自己的drawable, 因此, 要繼承

class TriangleDrawable extends android.graphics.drawable.Drawable {}

重寫他的draw方法

draw(canvas) {
  canvas.drawPath(this.path, paint);
}

畫筆創(chuàng)建一支就好, 因?yàn)闆](méi)有發(fā)現(xiàn)要?jiǎng)?chuàng)建多支畫筆的需求, 以后需要再改, 滿足當(dāng)下即可;

path肯定夠是變的, 因?yàn)榧^有上下兩個(gè)位置;

那么在這個(gè)TriangleDrawable類中, 我們要實(shí)現(xiàn)那些東西呢?

  • 設(shè)置箭頭方向 setDirection
  • 目前想不到別的了

如何確認(rèn)箭頭方向?

假設(shè)列表有ABC三條數(shù)據(jù), ABC依次排列, 在A的頂部, 如果有控件繼續(xù)放置一條數(shù)據(jù)D的話,

那么我們就把彈框菜單放到A的頂部, 如果沒(méi)有, 就放到A的底部

怎么判斷是否有足夠的空間放下D數(shù)據(jù)呢? 和那些東西有關(guān)?

  • 被長(zhǎng)按的view的頂部坐標(biāo)
  • 彈框菜單的高度

有這兩個(gè)信息, 我們就可以判斷箭頭的方向了.

為了判斷箭頭方向, 我們新建一個(gè)文件, getArrowDirection.js, 文件夾名popMenuRecyclerView, 和箭頭明顯不合適, 因此我們新建文件夾popMenuArrow

被長(zhǎng)按的view的頂部坐標(biāo)

view.getTop()

彈框菜單的高度, 因?yàn)閺椏蜻€沒(méi)有顯示出來(lái), 所以我們要預(yù)先測(cè)量他的高度

popWindow.getContentView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
let popupWindowHeight = popWindow.getContentView().getMeasuredHeight()

判斷箭頭指向

  if (longClickedViewTop - popupWindowHeight < 0) {
    // 上面放不下了, 菜單在下面出現(xiàn), 箭頭指向上方
    return "up";
  } else {
    return "down";
  }

我們給箭頭一個(gè)背景色, 先看當(dāng)前的效果

可以看到箭頭上下的效果已經(jīng)出來(lái)了,

箭頭View的挪動(dòng)使用了addView和removeView

let arrowView = popMenuWindow.findView("arrow");
popMenuWindow.findView("root").removeView(arrowView);
popMenuWindow.findView("root").addView(arrowView, 0);

這里有個(gè)問(wèn)題, 箭頭的背景色為什么那么長(zhǎng), 是彈框菜單的兩倍多.

這是因?yàn)镚ridLayoutManager第二個(gè)參數(shù)設(shè)置了5, 我們改為Math.min, 取最小值, 寬度問(wèn)題就符合預(yù)期了

const layoutManager = new GridLayoutManager(view.getContext(), Math.min(popMenus.length, 5));

調(diào)整popwindow的位置

如果彈框菜單在長(zhǎng)按控件的上方, 那么應(yīng)該偏移多少?

Y軸偏移量 = 彈框菜單的高度 + 長(zhǎng)按控件的高度

調(diào)用方法如下

    let offset = popMenuCalculateOffset(view, mPopWindow, arrowDirection);
    if (arrowDirection == "down") {
      console.log("箭頭朝下");
      mPopWindow.showAsDropDown(view, offset.x, offset.y);
    }

我們新建一個(gè)文件 popMenuCalculateOffset.js

module.exports = function popMenuCalculateOffset(longClickedView, popWindow, arrowDirection) {
  let contentView = popWindow.getContentView();
  let width = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
  let height = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
  contentView.measure(width, height);
  popWindow.setBackgroundDrawable(new ColorDrawable(0));
  let contentViewHeight = contentView.getMeasuredHeight();
  let longClickedViewHeight = longClickedView.getHeight();
  console.log("contentViewHeight = " + contentViewHeight);
  if (arrowDirection == "down") {
    let y = contentViewHeight + longClickedViewHeight;
    return { x: 0, y: -y };
  } else {
    return { x: 0, y: 0 };
  }
};

獲取高寬高以后, 我們的

    let offset = popMenuCalculateOffset(view, mPopWindow, arrowDirection);
    if (arrowDirection == "down") {
      console.log("箭頭朝下");
      mPopWindow.showAsDropDown(view, offset.x, offset.y);
    } else {
      let arrowView = popMenuWindow.findView("arrow");
      popMenuWindow.findView("root").removeView(arrowView);
      popMenuWindow.findView("root").addView(arrowView, 0);
      mPopWindow.showAsDropDown(view, offset.x, offset.y);
    }

代碼寫了不少了, 看看效果, 及時(shí)排查bug

箭頭朝上

箭頭朝下

繪制箭頭

我們用canvas畫個(gè)三角形, 首先我們要繼承類, 重寫他的draw方法

class TriangleDrawable extends android.graphics.drawable.Drawable {}

單獨(dú)寫一個(gè)類文件 TriangleDrawable.js, 放到文件夾 popMenuArrow;

繪制箭頭之前, 要知道箭頭的寬高, 和箭頭的中點(diǎn);

  • 箭頭的寬高, 我們就用arrowView的高度;
  • 箭頭的中點(diǎn), 我們指向被長(zhǎng)按的控件 X 軸的中心

為了使類, 盡可能的比較純, 我們傳遞的參數(shù)選擇具體的數(shù)值, 而不是控件;

這里的純指的是沒(méi)有副作用, 以及可復(fù)用的程度

class TriangleDrawable extends android.graphics.drawable.Drawable {
  setHeight(height) {
    this.height = height;
  }
  setWidth(width) {
    this.width = width;
  }
  setDirection(direction) {
    this.direction = direction;
  }
  setColor(color) {
    this.color = Color.parse(color).value;
  }
  setLongClickedViewWidth(longClickedViewWidth) {
    this.longClickedViewWidth = longClickedViewWidth;
  }
  draw(canvas) {
    trianglePath.reset();
    if (this.direction == "down") {
      console.log("down");
      trianglePath.moveTo(this.width / 2, this.height);
      trianglePath.lineTo(this.width / 2 - this.height / 2, 0);
      trianglePath.lineTo(this.width / 2 + this.height / 2, 0);
    } else {
      trianglePath.moveTo(this.width / 2, 0);
      trianglePath.lineTo(this.width / 2 - this.height / 2, this.height);
      trianglePath.lineTo(this.width / 2 + this.height / 2, this.height);
    }
    trianglePath.close();
    canvas.drawPath(trianglePath, paint);
  }
}
module.exports = TriangleDrawable;

在popupWindow出現(xiàn)之前, 我們要把箭頭繪制出來(lái),

await setArrowForeground(arrow, arrowDirection, view);
mPopWindow.showAsDropDown(view, offset.x, offset.y);

使用onPreDraw, 在繪制之前, 我們可以獲取到正確的寬高

  arrow.getViewTreeObserver().addOnPreDrawListener(
    new android.view.ViewTreeObserver.OnPreDrawListener({
      onPreDraw: function () {
        arrow.getViewTreeObserver().removeOnPreDrawListener(this);
        let arrowHeight = arrow.getHeight();
        let arrowWidth = arrow.getWidth();
        triangleDrawable.setWidth(arrowWidth);
        triangleDrawable.setHeight(arrowHeight);
        arrow.setForeground(triangleDrawable);
        return true;
      },
    })
  );

代碼寫了不少了, 先測(cè)試一下效果

箭頭朝上

箭頭朝下

修改顏色和圓角

顏色這個(gè)就不多說(shuō)了, 非常容易修改, 說(shuō)下圓角

修改圓角是在這個(gè)文件中: showMenuWindow.js, 我們要給RecyclerView包裹一層card

<card cardCornerRadius="8dp" w='wrap_content'>
...
</card>

給彈框菜單添加點(diǎn)擊事件

也就是給彈框菜單中的recyclerview添加點(diǎn)擊事件

增加點(diǎn)擊事件所在的文件是 popMenuRecyclerView/PopMenuRecyclerViewAdapter.js,

我們修改他的onCreateViewHolder

onCreateViewHolder(parent) {
  let testRecyclerViewViewHolder = new PopMenuRecyclerViewViewHolder(ui.inflateXml(parent.getContext(), holderXml, parent));
  testRecyclerViewViewHolder.itemView.setOnClickListener(() => {
    let item = this.data[testRecyclerViewViewHolder.getAdapterPosition()];
    item.handle();
    return true;
  });
  return testRecyclerViewViewHolder;
}

點(diǎn)擊事件生效了, 還有個(gè)問(wèn)題, 點(diǎn)擊了之后,彈框菜單沒(méi)有消失, 我們?cè)谶@里又引用不到彈框?qū)嵗? 怎么弄?

彈框菜單點(diǎn)擊事件引用彈框?qū)嵗?/h2>

我們可以用全局對(duì)象, 掛載彈框的實(shí)例;

我們不選怎全局對(duì)象, 而是去能引用的地方引用實(shí)例;

在 showMenuWindow.js 這個(gè)文件中, 出現(xiàn)了popupWindow實(shí)例, 我們把這個(gè)實(shí)例作為參數(shù), 傳遞給

setPopMenuRecyclerViewAdapter

setPopMenuRecyclerViewAdapter(mPopWindow, grid, popMenus);

setPopMenuRecyclerViewAdapter.js

module.exports = async function (mPopWindow, recyclerView, items) {
  const menuClick = (item, itemView) => {
    console.log(itemView);
    item.handle();
    mPopWindow.dismiss();
  };
  var adapter = new PopMenuRecyclerViewAdapter(items);
  adapter.setClick(menuClick);
  recyclerView.setAdapter(adapter);
};

我們?cè)谶@個(gè)文件中給adapter設(shè)置了點(diǎn)擊事件, 相應(yīng)的要在 PopMenuRecyclerViewAdapter.js 文件中添加方法,

setClick

class PopMenuRecyclerViewAdapter extends androidx.recyclerview.widget.RecyclerView.Adapter {
  constructor(data) {
    super();
    this.data = data;
    this.click = () => {};
  }
  onCreateViewHolder(parent) {
    let testRecyclerViewViewHolder = new PopMenuRecyclerViewViewHolder(ui.inflateXml(parent.getContext(), holderXml, parent));
    testRecyclerViewViewHolder.itemView.setOnClickListener(() => {
      let item = this.data[testRecyclerViewViewHolder.getAdapterPosition()];
      this.click(item, testRecyclerViewViewHolder.itemView);
      return true;
    });
    return testRecyclerViewViewHolder;
  }
  ...
  setClick(click) {
    this.click = click;
  }
}
module.exports = PopMenuRecyclerViewAdapter;

到這里就模仿的差不多了, 差不多就行.

如果要增加多個(gè)菜單, 在config.js中修改配置即可

環(huán)境

設(shè)備: 小米11pro
Android版本: 12
Autojs版本: 9.3.11

名人名言

思路是最重要的, 其他的百度, bing, stackoverflow, github, 安卓文檔, autojs文檔, 最后才是群里問(wèn)問(wèn) --- 牙叔教程

聲明

部分內(nèi)容來(lái)自網(wǎng)絡(luò) 本教程僅用于學(xué)習(xí), 禁止用于其他用途

以上就是autojs模仿QQ長(zhǎng)按彈窗菜單實(shí)現(xiàn)示例詳解二的詳細(xì)內(nèi)容,更多關(guān)于autojs模仿QQ長(zhǎng)按彈窗菜單的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論