برنامه نویسی

راهنمای عملی برای تهیه یک برنامه خبری بر اساس Harmonyos Next

1. بررسی اجمالی پروژه و تنظیم محیط

با انتشار Harmonyos Next ، توسعه دهندگان می توانند از قابلیت های توزیع شده پیشرفته خود و پیشرفت ابزار بهبود یافته برای ایجاد تجربیات کاربردی بین دستگاه استفاده کنند. این آموزش شما را از طریق تهیه یک برنامه خبری ، پوشش ماژول های اصلی مانند لیست اخبار ، مشاهده دقیق و عملکرد نشانک ، راهنمایی می کند.

ابتدا اطمینان حاصل کنید که محیط توسعه شما آماده است:

  1. آخرین نسخه استودیوی Deveco (توصیه شده 4.1 یا بالاتر) را نصب کنید.
  2. هنگام ایجاد یک پروژه ، “برنامه” → “توانایی خالی” را انتخاب کنید.
  3. پیکربندی خدمات اتصال AppGallery (جزئیات بعداً پوشش داده می شود).

ساختار پروژه در درجه اول از مؤلفه های اصلی زیر تشکیل شده است:

  • pages: حاوی کد صفحه است.
  • resources: پرونده های منابع برنامه را ذخیره می کند.
  • entryability: نقطه ورود برنامه.
  • model: لایه مدل داده.

2. ادغام سرویس AppGallery Connect

AppGallery Connect خدمات پشتیبان قوی را برای برنامه های Harmonyos ارائه می دهد. ابتدا تنظیمات زیر را تکمیل کنید:

  1. یک پروژه در کنسول AppGallery Connect ایجاد کنید.
  2. سرویس احراز هویت (سرویس AUTH) و پایگاه داده ابری (Cloud DB) را فعال کنید.
  3. پرونده پیکربندی را بارگیری کنید agconnect-services.json و آن را در پروژه قرار دهید entry دایرکتوری

وابستگی ها را اضافه کنید build.gradle:

// entry/build.gradle  
dependencies {  
    implementation 'io.agconnect.agconnect-core-harmony:agconnect-core:1.6.0.300'  
    implementation 'io.agconnect.agconnect-auth-harmony:agconnect-auth:1.6.0.300'  
    implementation 'io.agconnect.agconnect-clouddb-harmony:agconnect-clouddb:1.6.0.300'  
}  
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

کد اولیه سازی:

// entryability/EntryAbility.ts  
import agconnect from '@hw-agconnect/api-ohos';  
import '@hw-agconnect/core-ohos';  
import '@hw-agconnect/auth-ohos';  
import '@hw-agconnect/clouddb-ohos';  

onCreate() {  
    agconnect.instance().init(this.context);  
    // Initialize Cloud Database  
    this.initCloudDB();  
}  

async initCloudDB() {  
    try {  
        const cloudDBZoneConfig = new cloud.CloudDBZoneConfig('NewsDB');  
        this.cloudDBZone = await cloud.CloudDBZone.open(cloudDBZoneConfig);  
    } catch (err) {  
        console.error('CloudDB init failed: ' + JSON.stringify(err));  
    }  
}  
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

3. طراحی مدل داده های خبری

نوع شیء اخبار را برای ذخیره سازی محلی و ابری تعریف کنید. ایجاد کردن News.ts در model دایرکتوری:

// model/News.ts  
import { cloud } from '@hw-agconnect/clouddb-ohos';  

export class News {  
    // Use decorators to define Cloud DB fields  
    @cloud.Field()  
    id: string = '';  

    @cloud.Field()  
    title: string = '';  

    @cloud.Field()  
    content: string = '';  

    @cloud.Field()  
    author: string = '';  

    @cloud.Field()  
    publishTime: number = 0;  

    @cloud.Field()  
    category: string = '';  

    @cloud.Field()  
    imageUrl: string = '';  

    @cloud.Field()  
    isFavorite: boolean = false;  

    constructor() {  
        // Set object type name (must match the Cloud DB object type)  
        cloud.ObjectType.register(this, 'News');  
    }  
}  
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

پس از ایجاد نوع شیء مربوطه در کنسول Cloud DB ، می توانید عملیات CRUD را روی داده ها انجام دهید.

4. توسعه صفحه لیست اخبار

لیست اخبار صفحه اصلی برنامه است. ما از List مؤلفه برای نمایش داده های خبری با عملکرد پیمایش به Re-Refresh و Infinite.

// pages/NewsListPage.ets  
import { News } from '../model/News';  
import { cloud } from '@hw-agconnect/clouddb-ohos';  

@Entry  
@Component  
struct NewsListPage {  
    @State newsList: Array<News> = [];  
    @State isLoading: boolean = false;  
    @State isRefreshing: boolean = false;  
    private pageSize: number = 10;  
    private pageIndex: number = 0;  

    build() {  
        Column() {  
            // Header  
            Row() {  
                Text('Top News')  
                    .fontSize(24)  
                    .fontWeight(FontWeight.Bold)  
                Blank()  
                Image($r('app.media.ic_search'))  
                    .width(30)  
                    .height(30)  
                    .margin({ right: 15 })  
            }  
            .width('100%')  
            .padding(15)  
            .backgroundColor('#FF4D4F')  

            // News list  
            List({ space: 10 }) {  
                ForEach(this.newsList, (item: News) => {  
                    ListItem() {  
                        NewsItem({ news: item })  
                    }  
                }, (item: News) => item.id)  
            }  
            .width('100%')  
            .layoutWeight(1)  
            .onScrollIndex((start: number) => {  
                // Load more when scrolling to the bottom  
                if (start >= this.newsList.length - 3 && !this.isLoading) {  
                    this.loadMoreNews();  
                }  
            })  
            .scrollBar(BarState.Off)  
        }  
        .width('100%')  
        .height('100%')  
        .onAppear(() => {  
            this.refreshNews();  
        })  
    }  

    // Pull-to-refresh  
    async refreshNews() {  
        this.isRefreshing = true;  
        this.pageIndex = 0;  
        try {  
            const query = cloud.CloudDBZoneQuery.where(News).orderByDesc('publishTime').limit(this.pageSize);  
            const result = await this.cloudDBZone.executeQuery(query, News);  
            this.newsList = result;  
        } catch (err) {  
            console.error('Refresh news failed: ' + JSON.stringify(err));  
        }  
        this.isRefreshing = false;  
    }  

    // Load more  
    async loadMoreNews() {  
        this.isLoading = true;  
        this.pageIndex++;  
        try {  
            const query = cloud.CloudDBZoneQuery.where(News)  
                .orderByDesc('publishTime')  
                .limit(this.pageSize)  
                .offset(this.pageIndex * this.pageSize);  
            const result = await this.cloudDBZone.executeQuery(query, News);  
            this.newsList = this.newsList.concat(result);  
        } catch (err) {  
            console.error('Load more news failed: ' + JSON.stringify(err));  
        }  
        this.isLoading = false;  
    }  
}  

// News item component  
@Component  
struct NewsItem {  
    @Prop news: News;  

    build() {  
        Column() {  
            // News image  
            Image(this.news.imageUrl)  
                .width('100%')  
                .height(200)  
                .objectFit(ImageFit.Cover)  
                .borderRadius(8)  

            // Title and summary  
            Column() {  
                Text(this.news.title)  
                    .fontSize(18)  
                    .fontWeight(FontWeight.Bold)  
                    .margin({ bottom: 5 })  
                Text(this.news.content.substring(0, 50) + '...')  
                    .fontSize(14)  
                    .fontColor('#666')  
            }  
            .padding(10)  

            // Footer info  
            Row() {  
                Text(this.news.author)  
                    .fontSize(12)  
                    .fontColor('#999')  
                Blank()  
                Text(this.formatTime(this.news.publishTime))  
                    .fontSize(12)  
                    .fontColor('#999')  
                Image(this.news.isFavorite ? $r('app.media.ic_favorite') : $r('app.media.ic_favorite_border'))  
                    .width(20)  
                    .height(20)  
                    .margin({ left: 10 })  
                    .onClick(() => {  
                        this.toggleFavorite();  
                    })  
            }  
            .width('100%')  
            .padding({ left: 10, right: 10, bottom: 10 })  
        }  
        .width('95%')  
        .margin({ top: 5, bottom: 5 })  
        .backgroundColor('#FFF')  
        .borderRadius(8)  
        .shadow({ radius: 4, color: '#10000000', offsetX: 0, offsetY: 2 })  
        .onClick(() => {  
            router.pushUrl({ url: 'pages/NewsDetailPage', params: { newsId: this.news.id } });  
        })  
    }  

    // Format timestamp  
    private formatTime(timestamp: number): string {  
        const date = new Date(timestamp);  
        return `${date.getMonth() + 1}/${date.getDate()}`;  
    }  

    // Toggle bookmark  
    private async toggleFavorite() {  
        try {  
            this.news.isFavorite = !this.news.isFavorite;  
            await this.cloudDBZone.executeUpsert([this.news]);  
        } catch (err) {  
            console.error('Toggle favorite failed: ' + JSON.stringify(err));  
        }  
    }  
}  
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

5. اجرای صفحه جزئیات

صفحه جزئیات اخبار مطالب کامل را نشان می دهد و از نشانه گذاری پشتیبانی می کند.

// pages/NewsDetailPage.ets  
import { News } from '../model/News';  

@Entry  
@Component  
struct NewsDetailPage {  
    @State news: News = new News();  
    private newsId: string = '';  

    onPageShow() {  
        this.newsId = router.getParams()?.newsId;  
        this.loadNewsDetail();  
    }  

    build() {  
        Column() {  
            // Back button and title  
            Row() {  
                Image($r('app.media.ic_back'))  
                    .width(24)  
                    .height(24)  
                    .onClick(() => {  
                        router.back();  
                    })  
                Text('News Detail')  
                    .fontSize(20)  
                    .fontWeight(FontWeight.Bold)  
                    .margin({ left: 15 })  
                Blank()  
                Image(this.news.isFavorite ? $r('app.media.ic_favorite') : $r('app.media.ic_favorite_border'))  
                    .width(24)  
                    .height(24)  
                    .onClick(() => {  
                        this.toggleFavorite();  
                    })  
            }  
            .width('100%')  
            .padding(15)  
            .backgroundColor('#FF4D4F')  

            // Content area  
            Scroll() {  
                Column() {  
                    // Title and author info  
                    Text(this.news.title)  
                        .fontSize(22)  
                        .fontWeight(FontWeight.Bold)  
                        .margin({ bottom: 10 })  
                    Row() {  
                        Text(`Author: ${this.news.author}`)  
                            .fontSize(14)  
                            .fontColor('#666')  
                        Text(`Published: ${this.formatTime(this.news.publishTime)}`)  
                            .fontSize(14)  
                            .fontColor('#666')  
                            .margin({ left: 15 })  
                    }  
                    .margin({ bottom: 15 })  

                    // News image  
                    Image(this.news.imageUrl)  
                        .width('100%')  
                        .height(250)  
                        .objectFit(ImageFit.Cover)  
                        .margin({ bottom: 20 })  

                    // News content  
                    Text(this.news.content)  
                        .fontSize(16)  
                        .lineHeight(26)  
                }  
                .padding(20)  
            }  
            .scrollBar(BarState.Off)  
            .layoutWeight(1)  
        }  
        .width('100%')  
        .height('100%')  
    }  

    // Load news details  
    private async loadNewsDetail() {  
        try {  
            const query = cloud.CloudDBZoneQuery.where(News).equalTo('id', this.newsId);  
            const result = await this.cloudDBZone.executeQuery(query, News);  
            if (result && result.length > 0) {  
                this.news = result[0];  
            }  
        } catch (err) {  
            console.error('Load news detail failed: ' + JSON.stringify(err));  
        }  
    }  

    // Toggle bookmark  
    private async toggleFavorite() {  
        try {  
            this.news.isFavorite = !this.news.isFavorite;  
            await this.cloudDBZone.executeUpsert([this.news]);  
        } catch (err) {  
            console.error('Toggle favorite failed: ' + JSON.stringify(err));  
        }  
    }  

    // Format timestamp  
    private formatTime(timestamp: number): string {  
        const date = new Date(timestamp);  
        return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;  
    }  
}  
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

6. احراز هویت و نشانک کاربر

برای فعال کردن نشانه گذاری ، تأیید هویت کاربر با استفاده از سرویس AppGallery Connect Auth.

// utils/AuthUtil.ts  
import { agconnect } from '@hw-agconnect/api-ohos';  
import '@hw-agconnect/auth-ohos';  

export class AuthUtil {  
    // Anonymous login  
    static async anonymousLogin(): Promise<boolean> {  
        try {  
            const user = await agconnect.auth().signInAnonymously();  
            return user != null;  
        } catch (err) {  
            console.error('Anonymous login failed: ' + JSON.stringify(err));  
            return false;  
        }  
    }  

    // Get current user ID  
    static getCurrentUserId(): string | null {  
        const user = agconnect.auth().currentUser;  
        return user ? user.uid : null;  
    }  

    // Logout  
    static async logout(): Promise<void> {  
        try {  
            await agconnect.auth().signOut();  
        } catch (err) {  
            console.error('Logout failed: ' + JSON.stringify(err));  
        }  
    }  
}  
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اصلاح News مدل شامل انجمن کاربر:

// model/News.ts  
export class News {  
    // ...existing fields  

    @cloud.Field()  
    userId: string = ''; // Associated user ID  

    // ...other code  
}  
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

برای بررسی وضعیت ورود به سیستم ، نشانک را به روز کنید:

// In NewsItem component, modify toggleFavorite  
private async toggleFavorite() {  
    if (!AuthUtil.getCurrentUserId()) {  
        const isLogin = await AuthUtil.anonymousLogin();  
        if (!isLogin) {  
            prompt.showToast({ message: 'Please log in first' });  
            return;  
        }  
    }  

    try {  
        this.news.isFavorite = !this.news.isFavorite;  
        this.news.userId = AuthUtil.getCurrentUserId() || '';  
        await this.cloudDBZone.executeUpsert([this.news]);  
    } catch (err) {  
        console.error('Toggle favorite failed: ' + JSON.stringify(err));  
    }  
}  
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

7. بسته بندی برنامه و انتشار

پس از توسعه ، برنامه را به AppGallery بسته و منتشر کنید:

  1. در استودیوی Deveco ، Build را انتخاب کنید → تولید کلید و CSR برای ایجاد گواهی امضای.
  2. پیکربندی اطلاعات امضا در build-profile.json5بشر
  3. Build → Build HAP (ها)/برنامه (ها) را انتخاب کنید → برنامه ساخت برای تولید بسته نسخه.
  4. وارد کنسول AppGallery Connect شوید ، به “برنامه های من” بروید و یک برنامه جدید ایجاد کنید.
  5. پرونده برنامه تولید شده را بارگذاری کنید ، جزئیات برنامه را پر کنید و برای بررسی ارسال کنید.

8. پیشنهادات پیشرفته ویژگی

پس از اتمام اصول اولیه ، اضافه کردن را در نظر بگیرید:

  1. فیلتر دسته بندی: برای فیلتر کردن اخبار ، زبانه های دسته را اضافه کنید.
  2. نظرات: عملکرد نظر را با استفاده از Cloud DB پیاده سازی کنید.
  3. خواندن آفلاین: برای دسترسی آفلاین از فضای محلی استفاده کنید.
  4. اعلان ها را فشار دهید: ادغام کیت فشار AppGallery Connect برای شکستن هشدارهای خبری.
  5. همگام سازی چند دستگاهی: اهرم هارمونیوس قابلیت های توزیع شده برای تعویض دستگاه یکپارچه.

این آموزش یک راهنمای کامل برای تهیه یک برنامه خبری مبتنی بر Harmonyos ، از راه اندازی تا اجرای ارائه می دهد. از آن به عنوان پایه ای برای ساختن برنامه های پیشرفته تر استفاده کنید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا