博客 / 詳情

返回

鴻蒙開發實戰:玩轉“智感握姿”——新聞列表左右手智能切換

大家好,我是V哥。

你有沒有遇到過這種情況:

左手拿着奶茶,右手刷新聞,結果頭圖永遠在右邊,點都點不到?

現在好了,系統能實時感知你是左手還是右手握持,UI 自動適配!這才是真正的“懂你”!

今天 V 哥就用一個新聞列表頁面,帶你 10 分鐘搞定智感握姿的完整開發!能根據你拿手機的姿勢,自動把圖片和文字互換位置。代碼全在一個頁面,複製進去就能跑,絕對硬核!

技術原理:手機怎麼知道那是你的左手?

其實很簡單。你想想,當你用右手單手握持手機時,為了讓大拇指夠到屏幕左側,手機通常會不由自主地向左傾斜一點點(或者向右傾斜,看個人習慣,通常我們設定一個傾斜閾值)。

咱們利用鴻蒙的 @ohos.sensor(傳感器能力),監聽重力變化。

  • 當檢測到手機向左傾斜(X軸重力分量變化),判定為左手或左側模式。
  • 當檢測到手機向右傾斜,判定為右手或右側模式。

話不多説,直接上乾貨。

實戰代碼:智感握姿新聞列表

先看一下 V 哥寫的案例截圖:

左手模式:

05E884549A9AD41703DF2C542C399749

右手模式:

FEC1A0D1E197B7D8617DC5FBC20A1FB6

準備好你的 DevEco Studio,新建一個 ArkTS 頁面,把下面的代碼全選、複製、粘貼進去。

完整代碼案例

import sensor from '@ohos.sensor';
import promptAction from '@ohos.promptAction';

// 1. 定義新聞數據模型
class NewsItem {
  id: number;
  title: string;
  summary: string;
  imageColor: Color; // 用顏色塊代替圖片,方便測試,不用找資源

  constructor(id: number, title: string, summary: string, color: Color) {
    this.id = id;
    this.title = title;
    this.summary = summary;
    this.imageColor = color;
  }
}

@Entry
@Component
struct SmartGripNewsPage {
  // 2. 狀態變量
  // isRightMode: true 代表右手模式(圖在右),false 代表左手模式(圖在左)
  @State isRightMode: boolean = true;
  // 記錄當前的傾斜角度X值,用於顯示調試信息
  @State currentGravityX: number = 0;

  // 模擬新聞數據
  @State newsList: NewsItem[] = [
    new NewsItem(1, "鴻蒙Next正式發佈", "純血鴻蒙不再兼容安卓,開啓移動操作系統新紀元。", Color.Blue),
    new NewsItem(2, "V哥聊技術", "深度解析ArkTS語言特性,帶你彎道超車。", Color.Red),
    new NewsItem(3, "2026行業展望", "AI賽道爆發,普通程序員如何抓住最後的機會?", Color.Green),
    new NewsItem(4, "SpaceX星艦發射", "馬斯克火星殖民計劃又近了一步,震撼全人類。", Color.Orange),
    new NewsItem(5, "週末去哪兒玩", "發現城市周邊的小眾露營地,放鬆身心好去處。", Color.Pink),
  ];

  // 3. 頁面加載時開啓傳感器監聽
  aboutToAppear() {
    this.startSensor();
  }

  // 4. 頁面銷燬時關閉傳感器,省電
  aboutToDisappear() {
    this.stopSensor();
  }

  // 開啓傳感器邏輯
  startSensor() {
    try {
      // 監聽重力傳感器,頻率設置為 UI (適合UI交互的頻率)
      sensor.on(sensor.SensorId.GRAVITY, (data) => {
        // data.x 代表 x 軸的重力分量
        // 當手機豎屏面對你:
        // 手機向右傾斜,x > 0
        // 手機向左傾斜,x < 0
        
        this.currentGravityX = data.x;

        // 設置一個閾值,防止輕微抖動就切換
        // 這裏設置 1.5 為閾值,你可以根據手感調整
        if (data.x > 1.5) {
          // 向右傾斜,認為是右手握持或者想看右邊
          if (this.isRightMode === false) {
            this.isRightMode = true;
            this.showToast("智感切換:右手模式");
          }
        } else if (data.x < -1.5) {
          // 向左傾斜,認為是左手握持
          if (this.isRightMode === true) {
            this.isRightMode = false;
            this.showToast("智感切換:左手模式");
          }
        }
      }, { interval: 100000000 }); // 100ms 一次回調
    } catch (err) {
      console.error("V哥提示:傳感器啓動失敗,可能是模擬器不支持", err);
    }
  }

  // 關閉傳感器
  stopSensor() {
    try {
      sensor.off(sensor.SensorId.GRAVITY);
    } catch (err) {
      console.error("V哥提示:傳感器關閉失敗", err);
    }
  }

  // 小提示彈窗
  showToast(msg: string) {
    promptAction.showToast({
      message: msg,
      duration: 1500,
      bottom: 100
    });
  }

  build() {
    Column() {
      // 頂部標題欄
      Row() {
        Text("智感新聞")
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
        Blank()
        // 顯示當前模式狀態
        Text(this.isRightMode ? "當前:右手模式" : "當前:左手模式")
          .fontSize(14)
          .fontColor(Color.Gray)
      }
      .width('100%')
      .padding(20)
      .height(60)
      .backgroundColor('#F1F3F5')

      // 調試信息(正式上線可以去掉)
      Text(`重力X軸感應值: ${this.currentGravityX.toFixed(2)}`)
        .fontSize(12)
        .fontColor(Color.Gray)
        .margin({ bottom: 10 })

      // 新聞列表
      List({ space: 15 }) {
        ForEach(this.newsList, (item: NewsItem) => {
          ListItem() {
            // 核心佈局:根據 isRightMode 決定佈局方向
            // Direction.Ltr (Left to Right) 或者是 Rtl
            // 這裏我們用 Flex 或者 Row 手動控制順序更穩
            this.NewsItemBuilder(item)
          }
        })
      }
      .width('100%')
      .layoutWeight(1) // 佔滿剩餘空間
      .padding({ left: 15, right: 15 })
    }
    .width('100%')
    .height('100%')
  }

  // 自定義構建函數,處理單個新聞的佈局
  @Builder
  NewsItemBuilder(item: NewsItem) {
    Row() {
      // 這裏的邏輯:
      // 如果是左手模式(isRightMode=false),圖片在左,文字在右
      // 如果是右手模式(isRightMode=true),文字在左,圖片在右
      // 利用 Row 的 direction 屬性或者簡單的 if/else 渲染順序

      if (!this.isRightMode) {
        // 左手模式:圖 -> 文
        this.ImageBlock(item.imageColor)
        this.TextBlock(item)
      } else {
        // 右手模式:文 -> 圖
        this.TextBlock(item)
        this.ImageBlock(item.imageColor)
      }
    }
    .width('100%')
    .height(100)
    .backgroundColor(Color.White)
    .borderRadius(10)
    .shadow({ radius: 5, color: 0x1F000000, offsetY: 2 })
    .padding(10)
    // 添加一個順滑的動畫效果
    .animation({
      duration: 300,
      curve: Curve.EaseInOut
    })
  }

  // 抽取圖片組件
  @Builder
  ImageBlock(color: Color) {
    // 模擬圖片
    Stack() {
      Text("頭圖")
        .fontColor(Color.White)
        .fontSize(12)
    }
    .width(100)
    .height('100%')
    .backgroundColor(color)
    .borderRadius(8)
    .margin(this.isRightMode ? { left: 10 } : { right: 10 }) // 根據位置給間距
  }

  // 抽取文字組件
  @Builder
  TextBlock(item: NewsItem) {
    Column() {
      Text(item.title)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .width('100%')
      
      Text(item.summary)
        .fontSize(14)
        .fontColor(Color.Gray)
        .maxLines(2)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .margin({ top: 5 })
        .width('100%')
    }
    .layoutWeight(1) // 佔滿剩餘寬度
    .height('100%')
    .justifyContent(FlexAlign.Start)
    .alignItems(HorizontalAlign.Start)
  }
}

代碼深度解析(V哥掰碎了講)

兄弟們,代碼貼完了,V哥給你捋一捋這裏的核心門道,面試或者做項目的時候都能吹一波。

1. 傳感器監聽 (sensor.on)
這是整個功能的靈魂。我們用了 sensor.SensorId.GRAVITY

  • data.x 是關鍵。當你拿着手機往左歪(像是左手拿着手機想看左邊屏幕)時,X軸會變負數;往右歪時,X軸變正數。
  • 這裏我加了個閾值 1.5。為啥?如果不加閾值,你的手稍微抖一下,界面就左右亂跳,用户得氣死。1.5 是個經驗值,大約傾斜 15-20 度左右觸發,既靈敏又不會誤觸。

2. 狀態驅動 UI (@State isRightMode)
鴻蒙 ArkUI 的精髓就是狀態驅動

  • 我們不需要去手動搬運組件。只要改變 isRightMode 這個布爾值,UI 就會自動刷新。
  • 配合 .animation 屬性,當組件位置互換時,不會生硬地“閃現”,而是會有一個滑動的過渡效果,高級感立馬就來了。

3. 條件渲染 (if/else)
NewsItemBuilder 裏,V哥用了一個最笨但最有效的方法:

  • 如果是左手模式:先渲染圖片組件,再渲染文字組件。
  • 如果是右手模式:先渲染文字組件,再渲染圖片組件。
  • 因為是在 Row 容器裏,渲染順序直接決定了誰在左誰在右。

怎麼測試?

  1. 真機測試(推薦):把代碼燒錄到鴻蒙手機上。拿着手機向左傾斜一下,你會發現圖片“刷”一下跑到左邊了;向右傾斜一下,圖片又跑回右邊了。
  2. 模擬器測試:DevEco Studio 的模擬器通常有個“虛擬傳感器”面板。你可以手動拖動重力傳感器的 X 軸滑塊,模擬手機傾斜,看界面會不會變。

V哥的最後嘮叨

兄弟們,這個功能雖然代碼不多,但體現的是以人為本的設計思維。

這就是鴻蒙 Next 開發好玩的地方,硬件能力調用極其簡單。2026年,不管是做應用還是做系統,交互體驗永遠是核心競爭力。

趕緊把這代碼跑起來,以後老闆讓你做“適老化”或者“單手模式”,你把這個 Demo 一亮,絕對驚豔全場!祝大家發碼愉快,沒有 Bug!

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.