اشتراک‌ها
فریم ورکی برای ساخت وب اپلیکیشن هایی شبیه به IOS و Android

اگر بابت سختگیری‌های آپل امکان تولید اپلیکیشن IOS ندارید می‌تونید از این فریم ورک استفاده کنید و تحت وب اپلیکیشن‌های شبیه به آن درست کنید.

شما از طریق وب به دوربین و QR کد و همچنین مکان یاب دسترسی دارید و به کمک PWA میتونید برنامه تحت وب خودتون به یک آیکن بر روی گوشی تبدیل کنید و حس کار کردن با گوگل کروم و Safari را از کاربر بگیرید و برای برنامه خود صفحه‌ی Splash درست کنید و برای اون Notification ارسال کنید حتی اگر برنامه شما بسته باشد.

حتما مثال‌های فریم ورک را نگاه کنید


حتما درباره‌ی PWA  و امکانات آن تحقیق کنید

فریم ورکی برای ساخت وب اپلیکیشن هایی شبیه به IOS  و Android
نظرات نظرسنجی‌ها
به عنوان توسعه دهنده اندرویدی چه گوشی را انتخاب می کنید؟
یکی از مسائلی که بسیار مهم است صد در صد میزان محبوبیت گوشی مورد نظر در سطح جامعه است که باعث میشود تست گوشی بهتر صورت بگیرد.

 البته راحتی تست گوشی هم بی تاثیر نیست، به عنوان مثال: هوآوی که خودم به عنوان گوشی شخصی هم ازش استفاده میکنم به این صورت هست که صداقت خیلی زیادی از خود در هنگام لاگ زدن نشان می‌دهد و حتی مسائلی که اصلا برای من اهمیتی ندارد هم لاگ میکند به طوری که در اکلیپس آن قدر سریع لاگ صورت میگیرد که بافر خالی شده و فرصت دیدن چیزی دست نمی‌دهد ولی در اندروید استادیو این مورد کنترل بیشتری دارد ولی با این حال عملیات لاگ در سطح برنامه زیاد اتفاق می‌افتد ولی در گوشی سامسونگ قبلی که داشتم یا سونی که چند روز پیش تست کردم تنها لاگ خطای نمایشی توسط Force close نمایش داده شد.
 با توجه به جست و جوهای اندکی که داشتم متوجه شدم این مشکل بسیاری از کاربران این گوشی است. با این حال این گوشی مزایایی هم داشته است.

نکته بعدی که مهم میدانم این هست که به روز بودن گوشی در هنگام تست بسیار کاربردی است به عنوان نمونه زمانی که شما قصد دسترسی به حافظه خارجی داشته باشید یا تنطیمات سیستم را بخواهید تغییر دهید از api 23 به بعد این مورد نیاز به یک اجازه مجدد هنگام انجام عمل را دارد و تنها به مجوز داخل مانیفست اکتفا نمی‌کند. در صورتی که ورژن گوشی از این پایین‌تر باشد با چنین مسئله ای روبرو نخواهید شد مگر اینکه از قبل درباره این مورد اطلاع داشته باشید و حالا برای مسائل دیگر هم به همین صورت.
مطالب
آشنایی با سورس AndroidBreadCrumb

زمانی که سیستم عامل های GUI مثل ویندوز به بازار آمدند، یکی از قسمت‌های گرافیکی آن‌ها AddressBar   نام داشت که مسیر حرکت آن‌ها را در فایل سیستم نشان میداد و در سیستم عامل‌های متنی  CLI با دستور  cd یا pwd انجام می‌شد. بعدها در وب هم همین حرکت با نام BreadCrumb صورت گرفت که به عنوان مثال مسیر رسیدن به صفحه‌ی یک محصول یا یک مقاله را نشان می‌داد. در یک پروژه‌ی اندرویدی نیاز بود تا یک ساختار درختی را پیاده سازی کنم، ولی در برنامه‌های اندروید ایجاد یک درخت، کار هوشمندانه و مطلوبی نیست و روش کار به این صورت است که یک لیست از گروه‌های والد را نمایش داده و با انتخاب هر آیتم لیست به آیتم‌های فرزند تغییر میکند. حالا مسئله این بود که کاربر باید مسیر حرکت خودش را بشناسد. به همین علت مجبور شدم یک BreadCrumb را برای آن طراحی کنم که در زیر تصویر آن را مشاهده می‌کنید.


 از نکات جالب توجه در مورد این ماژول می‌توان گفت که قابلیت این را دارد تا تصمیمات خود را بر اساس اندازه‌های مختلف صفحه نمایش بگیرد. به عنوان مثال اگر آیتم‌های بالا بیشتر از سه عدد باشد و در صفحه جا نشود از یک مسیر جعلی استفاده می‌کند و همه‌ی آیتم‌ها با اندیس شماره 1 تا index-3 را درون یک آیتم با عنوان (...) قرار می‌دهد که من به آن می‌گویم مسیر جعلی. به عنوان نمونه مسیر تصویر بالا در صفحه جا شده است و نیازی به این کار دیده نشده است. ولی تصویر زیر از آن جا که مسیر، طول width صفحه نمایش رد کرده است، نیاز است تا چنین کاری انجام شود. موقعی‌که کاربر آیتم ... را کلیک کند، مسیر باز شده و به محل index-3 حرکت می‌کند. یعنی دو مرحله به عقب باز می‌گردد.


نگاهی به کارکرد ماژول 

قبل از توضیح در مورد سورس، اجازه دهید نحوه‌ی استفاده از آن را ببینیم.

این سورس شامل دو کلاس است که ساده‌ترین کلاس آن AndBreadCrumbItem می‌باشد که مشابه کلاس ListItem در بخش وب دات نت است و دو مقدار، یکی متن و دیگری Id را می‌گیرد:

سورس:

public class AndBreadCrumbItem {

    private int Id;
    private String diplayText;

    public AndBreadCrumbItem(int Id, String displayText)
    {
        this.Id=Id;
        this.diplayText=displayText;
    }
    public String getDiplayText() {
        return diplayText;
    }
    public void setDiplayText(String diplayText) {
        this.diplayText = diplayText;
    }
    public int getId() {
        return Id;
    }
    public void setId(int id) {
        Id = id;
    }
}

به عنوان مثال می‌خواهیم یک breadcrumb را با مشخصات زیر بسازیم:

AndBreadCrumbItem itemhome=new AndBreadCrumbItem(0,"Home");
AndBreadCrumbItem itemproducts=new AndBreadCrumbItem(12,"Products");
 AndBreadCrumbItem itemdigital=new AndBreadCrumbItem(15,"Digital");
AndBreadCrumbItem itemhdd=new AndBreadCrumbItem(56,"Hard Disk Drive");
حال از کلاس اصلی یعنی AndBreadCrumb استفاده می‌کنیم و آیتم‌ها را به آن اضافه می‌کنیم:
AndBreadCrumb breadCrumb=new AndBreadCrumb(this);

        breadCrumb.AddNewItem(itemhome);
        breadCrumb.AddNewItem(itemproducts);
        breadCrumb.AddNewItem(itemdigital);
        breadCrumb.AddNewItem(itemhdd);
به این نکته دقت داشته باشید که با هر شروع مجدد چرخه‌ی Activity، حتما شیء Context این کلاس را به روز نمایید تا در رسم المان‌ها به مشکل برنخورد. می‌توانید از طریق متد زیر context را مقداردهی نمایید:
breadCumb.setContext(this);
هر چند راه حل پیشنهادی این است که این کلاس را نگهداری ننماید و از یک لیست ایستا جهت نگهداری AndBreadCrumbItem‌ها استفاده کنید تا باهر بار  فراخوانی رویدادهای اولیه چون oncreate یا onstart و.. شی BreadCrumb را پر نمایید.

پس از افزودن آیتم ها، تنظیمات زیر را اعمال نمایید:

        LinearLayout layout=(LinearLayout)getActivity().findViewById(R.id.breadcumblayout);
        layout.setPadding(8, 8, 8, 8);
        breadCrumb.setLayout(layout);
        breadCrumb.SetTinyNextNodeImage(R.drawable.arrow);
        breadCrumb.setTextSize(25);
        breadCrumb.SetViewStyleId(R.drawable.list_item_style);
در سه خط اول، یک layout  از نوع Linear جهت رسم اشیاء به شیء breadcrumb معرفی می‌شود. سپس در صورت تمایل می‌توانید از یک شیء تصویر گرافیکی کوچک هم استفاده کنید که در تصاویر بالا می‌بینید از تصویر یک فلش جهت دار استفاده شده است تا بین هر المان ایجاد شده از آیتم‌ها قرار بگیرد. سپس در صورت تمایل اندازه‌ی قلم متون را مشخص می‌کنید و در آخر هم متد SetViewStyleId هم برای نسبت دادن یک استایل یا selector و ... استفاده می‌شود.
حال برای رسم آن متد UpdatePath را صدا می‌زنیم:
        breadCrumb.UpdatePath();

الان اگر برنامه اجرا شود باید breadcrumb از چپ به راست رسم گردد. برای استفاده‌های فارسی، راست به چپ می‌توانید از متد زیر استفاده کنید:
breadCrumb.setRTL(true);
در صورت هر گونه تغییری در تنظیمات، مجددا متد UpdatePath را فراخوانی کنید تا عملیات رسم، با تنظمیات جدید آغاز گردد.

در صورتیکه قصد دارید تنظیمات بیشتری چون رنگ متن، فونت متن و ... را روی هر المان اعمال کنید، از رویداد زیر استفاده کنید:

breadCrumb.setOnTextViewUpdate(new ITextViewUpdate() {
            @Override
            public TextView UpdateTextView(Context context, TextView tv) {
                tv.setTextColor(...);
                tv.setTypeface(...);
                return tv;
            }
        });
با هر بار ایجاد المان که از نوع TextView است، این رویداد فراخوانی شده و تنظیمات شما را روی آن اجرا می‌کند.
همچنین در صورتیکه می‌خواهید بدانید کاربر بر روی چه عنصری کلیک کرده است، از رویداد زیر استفاده کنید:
breadCumb.setOnClickListener(new IClickListener() {
            @Override
            public void onClick(int position, int Id) {
                  //...
            }
        });
کد بالا دو آرگومان را ارسال میکند که اولی position یا اندیس مکانی عنصر کلیک شده را بر می‌گرداند و دومی id هست که با استفاده ازکلاس AndBreadCrumbItem به آن پاس کرده‌اید. هنگام کلیک کاربر روی عنصر مورد نظر، برگشت به عقب به طور خودکار صورت گرفته و عناصر بعد از آن موقعیت، به طور خودکار حذف خواهند شد.

آخرین متد موجود که کمترین استفاده را دارد، متد SetNoResize است. در صورتیکه این متد با True مقداردهی گردد، عملیات تنظیم بر اساس صفحه‌ی نمایش لغو می‌شود. این متد برای زمانی مناسب است که به عنوان مثال شما از یک HorozinalScrollView استفاده کرده باشید. در این حالت layout شما هیچ گاه به پایان نمی‌رسد و بهتر هست عملیات اضافه را لغو کنید.

نگاهی به سورس

  کلاس زیر شامل بخش‌های زیر است:
فیلدهای خصوصی
 //=-=--=-=-=-=-=-=-=-=-=-=-=- Private Properties -=-=-=-=-=-=-=--=-=-=
    private List<AndBreadCrumbItem> items=null;
    private List<TextView> textViews;
    private int tinyNextNodeImage;
    private int viewStyleId;
    private Context context;
    private boolean RTL;
    private float textSize=20;
    private boolean noResize=false;

    LinearLayout layout;
    IClickListener clickListener;
    ITextViewUpdate textViewUpdate;
    LinearLayout.LayoutParams params ;

با نگاهی به نام آن‌ها میتوان حدس زد که برای چه کاری استفاده می‌شوند. به عنوان نمونه از اصلی‌ترین‌ها، متغیر items جهت نگهداری آیتم‌های پاس شده استفاده می‌شود و textviews هم برای نگهداری هر breadcrumb یا همان المان TextView که روی صفحه رسم می‌شود.
اینترفیس‌ها هم با حرف I شروع و برای تعریف رویدادها ایجاد شده‌اند. در ادامه از تعدادی متد get و Set برای مقدار دهی بعضی از فیلدهای خصوصی بالا استفاده شده است:
    //=-=---=-=-=-=-- Constructor =--=-=-=-=-=--=-=-

    public AndBreadCrumb(Context context)
    {
        this.context=context;
        params = new LinearLayout.LayoutParams
                (LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
    }

    //=-=-=--=--=-=-=-=-=-=-=-=-  Public Properties --=-=-=-=-=-=--=-=-=-=-=-=-

    //each category would be added to create path
    public void AddNewItem(AndBreadCrumbItem item)
    {
        if(items==null)
            items=new ArrayList<>();
        items.add(item);
    }

    // if you want a pointer or next node between categories or textviews
    public void SetTinyNextNodeImage(int resId) {this.tinyNextNodeImage=resId;}

    public void SetViewStyleId(int resId) {this.viewStyleId=resId;}

    public void setTextSize(float textSize) {this.textSize = textSize;}

    public boolean isRTL() {
        return RTL;
    }

    public void setRTL(boolean RTL) {
        this.RTL = RTL;
    }

    public void setLayout(LinearLayout layout) {

        this.layout = layout;
    }

    public void setContext(Context context) {
        this.context = context;
    }

    public boolean isNoResize() {
        return noResize;
    }

    public void setNoResize(boolean noResize) {
        this.noResize = noResize;
    }

بعد از آن به متدهای خصوصی می‌رسیم که متد زیر، متد اصلی ما برای ساخت breadcrumb است:
 //primary method for render objects on layout
    private void DrawPath() {


        //stop here if essentail elements aren't present
        if (items == null) return ;
        if (layout == null) return;
        if (items.size() == 0) return;


//we need to get size of layout,so we use the post method to run this thread when ui is ready
        layout.post(new Runnable() {
            @Override
            public void run() {


                //textviews created here one by one
                int position = 0;
                textViews = new ArrayList<>();
                for (AndBreadCrumbItem item : items) {
                    TextView tv = MakeTextView(position, item.getId());
                    tv.setText(item.getDiplayText());
                    textViews.add(tv);
                    position++;
                }


                //add textviews on layout
                AddTextViewsOnLayout();

                //we dont manage resizing anymore
                if(isNoResize()) return;

                //run this code after textviews Added to get widths of them
                TextView last_tv=textViews.get(textViews.size()-1);
                last_tv.post(new Runnable() {
                    @Override
                    public void run() {
                        //define width of each textview depend on screen width
                        BatchSizeOperation();
                    }
                });

            }
        });


    }
متد DrawPath برای ترسیم breadcumb است و می‌توان گفت اصلی‌ترین متد این کلاس است. در سه خط اول، عناصر الزامی را که باید مقداردهی شده باشند، بررسی می‌کند. این موارد وجود آیتم‌ها و layout است. اگر هیچ یک از اینها مقدار دهی نشده باشند، عملیات رسم خاتمه می‌یابد. بعد از آن یک پروسه‌ی UI جدید را در متد post شیء Layout معرفی می‌کنیم. این متد زمانی این پروسه را صدا می‌زند که layout در UI برنامه جا گرفته باشد. دلیل اینکار این است که تا زمانی که ویوها در UI تنظیم نشوند، نمی‌توانند اطلاعاتی چون پهنا و ارتفاع را برگردانند و همیشه مقدار 0 را باز می‌گردانند. پس ما بامتد post اعلام می‌کنیم زمانی این پروسه را اجرا کن که وضعیت UI خود را مشخص کرده‌ای.
به عنوان نمونه کد زیر را ببینید:
TextView tv=new TextView(this);
tv.getWidth(); //return 0
layout.add(tv);
tv.getWidth(); //return 0
در این حالت کنترل در هر صورتی عدد ۰ را به شما باز می‌گرداند و نمی‌توانید اندازه‌ی آن را بگیرید مگر اینکه درخواست یک callback بعد از رسم را داشته باشید که این کار از طریق متد post انجام می‌گیرد:
TextView tv=new TextView(this);
tv.post(new Runnable() {
                    @Override
                    public void run() {
                        tv.getWidth(); //return x
                    }
                });
در اینجا مقدار واقعی x بازگردانده می‌شود.

باز می‌گردیم به متد DrawPath و داخل متد post
 در اولین خط این پروسه به ازای هر آیتم، یک TextView توسط متد MakeTextView ساخته می‌شود که شامل کد زیر است:
  private TextView MakeTextView(final int position, final int Id)
    {
        //settings for cumbs
        TextView tv=new TextView(this.context);
        tv.setEllipsize(TextUtils.TruncateAt.END);
        tv.setSingleLine(true);
        tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
        tv.setBackgroundResource(viewStyleId);

        /*call custom event - this event will be fired when user click on one of
         textviews and returns position of textview and value that user sat as id
         */
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                SetPosition(position);
                clickListener.onClick(position, Id);
            }
        });

        //if user wants to update each textviews
        if(textViewUpdate!=null)
            tv=textViewUpdate.UpdateTextView(context,tv);

        if(isRTL())
            tv.setRotationY(180);

        return tv;
    }

در خطوط اولیه، یک Textview ساخته و متد Ellipsize را با Truncate.END مقداردهی می‌نماید. این مقدار دهی باعث می‌شود اگر متن، در Textview جا نشد، ادامه‌ی آن با ... مشخص شود. در خط بعدی Textview را تک خطه معرفی می‌کنیم. در خط بعدی اندازه‌ی قلم را بر اساس آنچه کاربر مشخص کرده است، تغییر می‌دهیم و بعد هم استایل را برای آن مقداردهی می‌کنیم. بعد از آن رویداد کلیک را برای آن مشخص می‌کنیم تا اگر کاربر بر روی آن کلیک کرد، رویداد اختصاصی خودمان را فراخوانی کنیم.
در خط بعدی اگر rtl با true مقدار دهی شده باشد، textview را حول محور Y چرخش می‌دهد تا برای زبان‌های راست به چپ چون فارسی آماده گردد و در نهایت Textview ساخته شده و به سمت متد DrawPath باز می‌گرداند.

بعد از ساخته شدن TextViewها، وقت آن است که به Layout اضافه شوند که وظیفه‌ی اینکار بر عهده‌ی متد AddTextViewOnLayout است:
 //this method calling by everywhere to needs add textviews on the layout like master method :drawpath
    private void AddTextViewsOnLayout()
    {
        //prepare layout
        //remove everything on layout for recreate it
        layout.removeAllViews();
        layout.setOrientation(LinearLayout.HORIZONTAL);
        layout.setVerticalGravity(Gravity.CENTER_VERTICAL);
        if(isRTL())
            layout.setRotationY(180);



        //add textviews one by one

        int position=0;
        for (TextView tv:textViews)
        {
            layout.addView(tv,params);

            //add next node image between textviews if user defined a next node image
            if(tinyNextNodeImage>0)
                if(position<(textViews.size()-1)) {
                    layout.addView(GetNodeImage(), params);
                    position++;
                }
        }

    }

در چند خط اول، Layout آماده سازی می‌شود. این آماده سازی شامل پاکسازی اولیه Layout یا خالی کردن ویوهای درون آن است که می‌تواند از رندر قبلی باشد. افقی بودن جهت چینش Layout، در مرکز نگاه داشتن ویوها و نهایتا چرخش حول محور Y در صورت true بودن خاصیت RTL است. در خطوط بعدی یک حلقه وجود دارد که Textview‌های ایجاد شده را یک به یک در Layout می‌چیند و اگر کاربر تصویر گرافیکی را هم به (همان فلش‌های اشاره‌گر) متغیر tinyNextNodeImage نسبت داده باشد، آن‌ها را هم بین TextView‌ها می‌چیند و بعد از پایان یافتن کار، مجددا به متد DrawPath باز می‌گردد.
تا به اینجا کار چیدمان به ترتیب انجام شده است ولی از آنجا که اندازه‌ی Layout در هر گوشی و  در دو حالت حالت افقی یا عمودی نگه داشتن گوشی متفاوت است، نمی‌توان به این چینش اعتماد کرد که به چه نحوی عناصر نمایش داده خواهند شد و این مشکل توسط متد BatchSizeOperation (تغییر اندازه دسته جمعی) حل می‌گردد. در اینجا هم باز متد post به آخرین textview اضافه شده است. به این علت که موقعی‌که همه‌ی textview‌ها در ui جا خوش کردند، بتوانیم به خاصیت‌های ui آن‌ها دستیابی داشته باشیم. حالا بعد از ترسیم باید اندازه آن‌ها را اصلاح کنیم. قدم به قدم متد BatchSizeOperation را بررسی می‌کنیم:
//set textview width depend on screen width
private void BatchSizeOperation()
{
//get width of next node between cumbs
Bitmap tinyBmap = BitmapFactory.decodeResource(context.getResources(), tinyNextNodeImage);
int tinysize=tinyBmap.getWidth();
//get sum of nodes
tinysize*=(textViews.size()-1);
...
}
ابتدا لازم است ‍‍‍‍‍طول مسیری که همه ویوها یا المان‌های ما را دارند، به دست آوریم. اول از تصویر کوچک شروع می‌کنیم و پهنای آن را می‌گیریم. سپس عدد به دست آمده را در تعداد آن ضرب می‌کنیم تا جمع پهناها را داشته باشیم. سپس نوبت به TextView‌ها می‌رسد.

  //get width size of screen(layout is screen here)
        int screenWidth=GetLayoutWidthSize();

        //get sum of arrows and cumbs width
        int sumtvs=tinysize;
        for (TextView tv : textViews) {

            int width=tv.getWidth();
            sumtvs += width;
        }
در ادامه‌ی این متد، متد GetLayoutWidthSize را صدا می‌زنیم که وظیفه‌ی آن برگرداندن پهنای layout است و کد آن به شرح زیر است:
    private int GetLayoutWidthSize()
    {
        int width=layout.getWidth();
        int padding=layout.getPaddingLeft()+layout.getPaddingRight();
        width-=padding;
        return width;
    }
در این متد پهنا به احتساب padding‌های چپ و راست به دست می‌آید و مقدار آن را به عنوان اندازه‌ی صفحه نمایش، تحویل متد والد می‌دهد. در ادامه هم پهنای هر Textview محاسبه شده و جمع کل آن‌ها را با اندازه‌ی صفحه مقایسه می‌کند. اگر کوچکتر بود، کار این متد در اینجا تمام می‌شود و نیازی به تغییر اندازه نیست. ولی اگر نبود کد ادامه می‌یابد:
    private void  BatchSizeOperation()
    {
        ....

    //if sum of cumbs is less than screen size the state is good so return same old textviews
        if(sumtvs<screenWidth)
            return ;


        if(textViews.size()>3)
        {
            //make fake path
            MakeFakePath();

            //clear layout and add textviews again
            AddTextViewsOnLayout();
        }

        //get free space without next nodes -> and spilt rest of space to textviews count to get space for each textview
        int freespace =screenWidth-tinysize;
        int each_width=freespace/textViews.size();

        //some elements have less than each_width,so we should leave size them and calculate more space again
        int view_count=0;
        for (TextView tv:textViews)
        {
            if (tv.getWidth()<=each_width)
                freespace=freespace-tv.getWidth();
            else
                view_count++;
        }
        if (view_count==0) return;

        each_width=freespace/view_count;
        for (TextView tv:textViews)
        {
            if (tv.getWidth()>each_width)
                tv.setWidth(each_width);
        }


    }

اگر آیتم‌ها بیشتر از سه عدد باشند، می‌توانیم از حالت مسیر جعلی استفاده کنیم که توسط متد MakeFakePath انجام می‌شود. البته بعد از آن هم باید دوباره view‌ها را چینش کنیم تا مسیر جدید ترسیم گردد، چون ممکن است بعد از آن باز هم جا نباشد یا آیتم‌ها بیشتر از سه عدد نیستند. در این حالت، حداقل کاری که می‌توانیم انجام دهیم این است که فضای موجود را بین آن‌ها تقسیم کنیم تا همه‌ی کاسه، کوزه‌ها سر آیتم آخر نشکند و متنش به ... تغییر یابد و حداقل از هر آیتم، مقداری از متن اصلی نمایش داده شود. پس میانگین فضای موجود را گرفته و بر تعداد المان‌ها تقسیم می‌کنیم. البته این را هم باید در نظر گرفت که در تقسیم بندی، بعضی آیتم‌ها آن مقدار پهنا را نیاز ندارند و با پهنای کمتر هم می‌شود کل متنشان را نشان داد. پس یک کار اضافه‌تر این است که مقدار پهنای اضافی آن‌ها را هم حساب کنیم و فقط آیتم‌هایی را پهنا دهیم که به مقدار بیشتری از این میانگین احتیاج دارند. در اینجا کار به پایان می‌رسد و مسیر نمایش داده می‌شود.

نحوه‌ی کارکرد متد MakeFakePath بدین صورت است که 4 عدد TextView را ایجاد کرده که المان‌های با اندیس 0 و 2 و 3 به صورت نرمال و عادی ایجاد شده و همان کارکرد سابق را دارند. ولی المان شماره دو با اندیس 1 با متن ... نماینده‌ی آیتم‌های میانی است و رویدادکلیک  آن به شکل زیر تحریف یافته است:

 //if elements are so much(mor than 3),we make a fake path to decrease elements
    private void MakeFakePath()
    {
        //we make 4 new elements that index 1 is fake element and has a rest of real path in its heart
        //when user click on it,path would be opened
        textViews=new ArrayList<>(4);
        TextView[] tvs=new TextView[4];
        int[] positions= {0,items.size()-3,items.size()-2,items.size()-1};

        for (int i=0;i<4;i++)
        {
            //request for new textviews
            tvs[i]=MakeTextView(positions[i],items.get(positions[i]).getId());

            if(i!=1)
                tvs[i].setText(items.get(positions[i]).getDiplayText());
            else {
                tvs[i].setText("...");
                //override click event and change it to part of code to open real path by call setposition method and redraw path
                tvs[i].setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int pos = items.size() - 3;
                        int id = items.get(pos).getId();
                        SetPosition(items.size() - 3);
                        clickListener.onClick(pos, id);
                    }
                });
            }
            textViews.add(tvs[i]);
        }
    }
این رویداد با استفاده از setPosition به آیتم index-3 بازگشته و مجددا المان‌ها رسم می‌گردند و سپس رویداد کلیک این آیتم را هم اجرا می‌کند و المان‌های با اندیس 2 و 3 را به ترتیب به رویدادهای index-1 و index-2 متصل می‌کنیم.
مطالب
معماری پایگاه داده چند مستاجری (Multi-Tenant Data Architecture)
 اعتماد و یا فقدان آن، عامل شماره یک مسدود کردن استفاده از نرم افزار به عنوان خدمات است. معماری پایگاه داده چند مستاجری برای رسیدگی به مشکل نرم افزار به عنوان سرویس (SaaS) که می‌تواند خدمات به تعدادی کلاینت ارائه کند استفاده می‌شود . معماری دیتابیس چند مستاجری وقتی مفید است که یک نمونه از دیتابیس به تعدادی کلاینت خدمات دهد. وقتی که نرم افزار‌های محلی نصب می‌کنید نرم افزارهای به عنوان یک سرویس با مشتریان متمرکز، دسترسی به داده‌ها مبتنی بر شبکه با سربار کمتر را فراهم می‌کنند. اما به منظور برخورداری بیشتر از مزیت‌های یک نرم افزار سرویس، یک سازمان باید از سطحی از کنترل روی داده صرفنظر کند و به فروشنده نرم افزار جهت نگهداری و امنیت به دور از چشم آنها اعتماد کند.

برای به درست آوردن این اعتماد، یکی از بالاترین الویت ها، آینده نگری معماری نرم افزار و ساخت یک معماری داده است که باید هر دو قوی و به اندازه کافی ایمن باشد، این دو برای راضی کردن مستاجران و کلاینت هایی که علاقمند هستند کنترل داده‌های حیاتی تجارت خود را به شخص سومی واگذار نمایند،  موثر است  در حالی که برای اداره کردن و نگهداری مقرون به صرفه است. 

  سه روش مدیریت چند مستاجری داده
  1. دیتابیس‌های جداگانه برای هر مستاجر
  2. دیتابیس مشترک و schema جداگانه برای هر مستاجر
  3. دیتابیس مشترک و schema مشترک 

  انتخاب روش مناسب برای برنامه شما به عوامل زیر بستگی دارد :
  • سایز دیتابیس هر مستاجر
  • تعداد مستاجران
  • تعداد کاربران هر مستاجر
  • نرخ رشد مستاجر
  • نرخ رشد دیتابیس مستاجر
  • امنیت  
  • هزینه

1) دیتابیس‌های جداگانه برای هر مستاجر :
ذخیره سازی داده‌های مستاجران در دیتابیس‌های جداگانه ساده‌ترین روش است. در این روش هر مستاجر یک دیتابیس دارد. منابع و کدهای برنامه معمولا در سرور بین همه مستاجران مشترک است اما هر مستاجر مجموعه ای از داده دارد که بطور منطقی از سایر مستاجران جدا شده است.

  مزایا :
  • امنیت بیشتر
  • سهولت سفارشی سازی برای هر مستاجر
  • سهولت نگهداری ( Backup و Restore ) برای هر مستاجر

معایب:
  • برای نگهداری سخت افزار قوی مورد نیاز است
  • این روش هزینه بیشتری برای تجهیزات ( Backup و Restore ) برای هر مستاجر دارد

  2)   دیتابیس مشترک و schema جداگانه برای هر مستاجر :
خدمات دهی به چندین مستاجر در یک دیتابیس مشترک اما هر مستاجر یک مجموعه از جداول گروهبندی شده دارد که با Schema جدا شده است که برای هر مستاجر الزامی است.

مزایا :
  • برای دیتابیس برنامه‌های کوچک مناسب است. وقتی تعداد جداول برای هر مشتری کم است
  • هزینه کمتری نسبت به روش اول دارد
  • برای مشتریانی که نگران امنیت هستند، سطح منطقی مناسبی برای جداسازی داده ه وجود دارد

معایب:
  • اطلاعات مستاجران در صورت بروز خطا به سختی restore می‌شود
  • مدیریت آن برای دیتابیس‌های بزرگ مشکل است

  3)   دیتابیس مشترک و schema مشترک :

این روش شمامل یک دیتابیس و یک مجموعه از جداول برای چندین مستاجر است. داده‌های جدول می‌تواند شامل رکورد‌های هر مستاجر باشد

مزایا :
  • در مقایسه با روش قبلی، کمترین هزینه سخت افزاری را دارد
  • می‌تواند مستاجران بیشتری رادر هر سرور پشتیبانی کند
  • قابلیت بروز رسانی آسان در یک جا برای همه مستاجران
  • مدیریت آسان دیتابیس و خطا و Backup و Restore
معایب:
  • امنیت بیشتری مورد نیاز است تا مطمئن شوید هیچکس به اطلاعات سایر مستاجران دسترسی ندارد.
  • می‌تواند روی کارایی کوئری‌ها تاثیر بگذارد چون تعداد رکورد‌ها زیاد است.
  • بروزرسانی و سفارشی کردن فقط برای یک مستاجر سخت است
 
منابع :
http://msdn.microsoft.com/en-us/library/aa479086.aspx
http://www.codeproject.com/Articles/51334/Multi-Tenants-Database-Architecture   
پروژه‌ها
بات تلگرامی پیدا کردن افردا در شعاع 10 کیلومتری
با توجه به بات‌های موجود در زمینه چت و  جستجو افراد تصمیم گرفتم پروژه مهندسی نرم افزاری خود را شبیه بات‌های موجود بعلاوه امکانات اضافه‌تر و ایده‌ای جدید بنویسم.
GoogleMapBot تنها یک بات تلگرامی نمیباشد. شما با دانلود سورس کد پروژه میتوانید دستگاه گوشی خودتان را وصل کنید و از همان سرویس استفاده کنید.
همانطور که از نام بات مشخص میباشد Googlemap بخش مهمی در این پروژه ایفا میکند.
کاربران آنلاین بات به محض لاگین شدن در بات، عکس پروفایل و مکان فعلی خود و سایر کاربران را میتوانند به صورت آنی بر روی صفحه html مشاهده نمایند.
سمت سرور پروژه با Web Api  و کلاینت آن با جی‌کوئری ایجکس پیاده سازی شده است. همچین با استفاده ازSignalR کاربران را هنگام ورود و خروج بر روی گوگل مپ به صورت آنی مشخص کرده‌ایم.
از امکانات آن میتوان به موارد زیر اشاره کرد
  • چت روم: هر منطقه دارای یک چت روم میباشد که بدون تداخل با همدیگر میتوانند کار خود را انجام دهند.
  • وب سایت: بات داری وبسایت میباشد که در بات میتوان با کلیک بر دکمه آن وارد وبسایت شد و درون وب میتوان نظرات خود را برای ادمین فرستاد.
  • ذخیره شدن  اطلاعات کاربران: تمام چت‌ها، عکس‌ها و حتی مکان‌های کاربر در بانک ذخیره میشود.
  • قابلیت آپدیت:  هنگام آپدیت برنامه، کاربران کاملا مدیریت شده و تداخلی در برنامه ایجاد نخواهد شد و پیغامی به تمام کاربران فرستاده میشود.
  • زمان بندی کاربران:  امکانی که در هیچ کدام از بات‌های دیگر موجود نیست، مدیریت کاربران آنلاین  میباشد. GooglemapBot با استفاده از  الگوریتمی که برای آن طراحی شده است، میتواند بعد از زمان مشخصی در صورتیکه کاربر فعالیت نداشته باشد، او را از بات خارج کند. 
  • نمایش افراد آنلاین: یکی دیگر از قابلیت‌های بات، امکان نمایش کاربران بر روی نقشه میباشد. هر کاربری بعد از ورود و خروج به صورت آنی با عکس پروفایلش بر روی نقشه نمایش داده میشود.
  •  محاسبه دقیق فاصله: هنگام ورود به بات، فاصله دقیق کاربر تا تهران محاسبه شده و نمایش داده میشود.
  • پنل مدیریت: داخل بات مدیر با وارد کردن پسورد خود شاهد پنل مدیریتی خواهد بود و با خروج پنل بات نمایش داده میشود.
امکانات بالا جزو سرویس‌هایی بودند که در بات‌های مشابهی که مشاهده کردم، وجود نداشتند و از  ذکر امکانات مشابه خوداری کردم. شما میتوانید سورس آن را دانلود کنید و آن را به شیوه دلخواه خود طراحی کنید. 
تصاویر برنامه


توجه کنید برای راه اندازی بات میتوانید از این لینک استفاده کنید.
مطالب
ایجاد گزارشات Crosstab در PdfReport
پیشنیازها
تهیه گزارشات Crosstab به کمک LINQ 
تهیه گزارشات Crosstab به کمک LINQ - قسمت دوم 

در دنیای واقعی، تهیه گزارشات به سادگی که در اکثر نرم افزارهای گزارش ساز موجود پیش بینی شده است، نیست. در این نوع نرم افزارها، بر اساس یک طراح بصری، تعدادی ستون مشخص، ایجاد شده، منبع داده‌ای به این ستون‌ها متصل و نهایتا گزارش تهیه می‌شود. اما اگر همین گزارش دارای تعداد ستون‌های متغیری باشد، اغلب این برنامه‌ها ناکارآمد خواهند بود. برای مثال لیست حضور و غیاب دانش آموزان را درنظر بگیرید.
اگر معلمی بخواهد سه روز در هفته را گزارش بگیرد، گزارش نیاز به سه ستون خواهد داشت. اگر 20 روز قبل مد نظر باشد، 20 ستون و همینطور الی آخر.
یا نمونه‌ی دیگری از این دست، گزارش حضور و غیاب پرسنل است. در اینجا بر اساس تعداد باری که شخص کارت می‌زند، ورود و خروج او محاسبه می‌شود. این مورد هم تعداد ثابتی نیست. ممکن است یک نفر 8 بار در طول روز کارت بزند، یک نفر فقط دو بار. به علاوه جمع ساعات هم اینجا دیگر عددی نیست و نیاز به فرمول خاصی دارد.

روش حل این نوع مسایل را در PdfReport در مثال‌های جدید ذیل می‌توانید مشاهده کنید:
1) DynamicCrosstab (یک گزارش Crosstab با تعداد ستون متغیر)



2) WorkedHours (یک گزارش Crosstab با تعداد ستون متغیر به علاوه تابع تجمعی سفارشی محاسبه جمع ساعات اشخاص)



3) ExtraHeadingCells (یک گزارش Crosstab به همراه ردیف‌های header اضافی و نحوه تعریف آن)



4) ExpensesCrosstab (یک گزارش Crosstab کلاسیک)



5) PdfA (یک گزارش Crosstab که به صورت استاندارد PdfA تهیه شده است. PdfA حالت خاصی از استاندارد PDF است که برای مستند سازی عموما مورد استفاده قرار می‌گیرد. رمزنگاری اطلاعات در آن ممنوع است. تصاویر بکارگرفته شده نباید شفاف باشند. قلم‌های مورد استفاده حتما باید در فایل مدفون شوند و مواردی از این دست)
 

نظرات مطالب
Implementing second level caching in EF code first
- همین پیاده سازی فوق رو با یک برنامه کنسول ویندوزی هم تست کردم، کار می‌کنه. می‌خواهید یک امتحانی بکنید. به نظر در پشت صحنه به صورت خودکار به memory cache سوئیچ میشه. فقط باید ارجاعی را به اسمبلی System.Web اضافه کنید.
- ضمن اینکه در برنامه‌های دسکتاپ این مساله اهمیت آنچنانی نداره؛ چون سطح دوم کش بیشتر جهت ارائه محتوایی یکسان و با دسترسی عمومی، به کاربران همزمان سایت کاربرد داره. عمده اطلاعات برنامه‌های دسکتاپ با سطح دسترسی خصوصی و مخصوص به یک کاربر است؛ در یک چنین مواردی نباید از سطح دوم کش استفاده کرد وگرنه به مشکلات امنیتی و فاش سازی اطلاعاتی که نباید عمومی شوند، منتهی خواهد شد (البته اگر مثلا از یک وب سرویس استفاده شده باشه؛ اگر همه چیز لوکال است، این مساله صادق نخواهد بود؛ اما باز هم نیازی به سطح دوم کش نیست. چون مهم‌ترین هدف آن کاهش بار بانک اطلاعاتی، در مراجعات مکرر کاربران است؛ که در حالت لوکال آنچنان معنی ندارد).
مطالب
اهمیت Controller های ساده در ASP.NET MVC
Controller‌ها به نوعی رابط بین View و Model هستند. ساده ترین محل برای قرار دادن کد‌های تصمیم گیری (decision-making code) ، قرار دادن منطق تجاری و یا فراهم ساختن داده برای View مثل ایجاد یک لیست از Select List برای یک DropDownList می‌باشند. اما انجام این کار‌ها به نرم افزار ما پیچیدگی تحمیل می‌کند. Controller‌ها باید در طول زمان توسعه‌ی یک نرم افزار کم حجم و سبک باقی بمانند. در این مطلب  بحث شد که یکی از اهداف استفاده از ASP.NET MVC نوشتن نرم افزار هایی تست پذیر می‌باشد. نوشتن آزمون واحد و نگهداری Controller هایی که مسئولیت زیادی بر عهده دارند سخت می‌باشد. Controller‌ها باید به نحوی توسعه پیدا کنند که نگهداری آن‌ها ساده باشد ، تست پذیر باشند و اصل SRP  را رعایت کرده باشند.
نگهداری آسان
کدی که درک آن سخت باشد ، تغییر دادن آن نیز سخت است ، هنگامی که درک کدی سخت باشد زمینه برای به وجود آمدن خطاها ، دوباره کاری و سردرد فراهم می‌شود. هنگامی که مسئولیت یک Action به صورت شفاف مشخص نباشد و انواع و اقسام کار‌ها به آن سپرده شده باشد تغییر در آن سخت می‌شود ،  ممکن است این تغییر باید چند جای دیگر هم داده شود در نتیجه فاز نگهداری هزینه و زمان اضافی به نرم افزار تحمیل می‌کند.
تست پذیری
 بهترین راه برای اطمینان از این که درک و تست پذیری سورس کد ما ساده هست انجام و تمرین توسعه‌ی تست محور (TDD) می‌باشد. هنگامی که از روش TDD استفاده می‌کنیم با سورسی کدی کار می‌کنیم که هنوز وجود ندارد. در این مرحله از به وجود آمدن کلاس هایی که تست آن‌ها دشوار یا غیر ممکن است (به دلیل داشتن مسئولیت‌های اضافی) از جمله Controller‌ها جلوگیری می‌شود. نوشتن آزمون‌های واحد برای Controller‌های کم حجم ساده می‌باشد. 
تمرکز بر روی یک مسئولیت
 بهترین راه برای ساده سازی Controller‌ها گرفتن مسئولیت‌های اضافی از آن می‌باشد  به Action زیر توجه کنید :
  
public RedirectToRouteResult Ship(int orderId)
{
   User user = _userSession.GetCurrentUser();
   Order order = _repository.GetById(orderId);
   if (order.IsAuthorized)                                
   {
      ShippingStatus status = _shippingService.Ship(order);
      if (!string.IsNullOrEmpty(user.EmailAddress))      
      {
         Message message = _messageBuilder
            .BuildShippedMessage(order, user);
         _emailSender.Send(message);
      }
      if (status.Successful)
      {
         return RedirectToAction("Shipped", "Order", new {orderId});
      }
   }
   return RedirectToAction("NotShipped", "Order", new {orderId});
}
 این Action کار زیادی انجام می‌دهد ، تقریبا می‌توان تعداد مسئولیت‌های این Action را با شمارش تعداد If‌ها پیدا کرد . مسئولیت تصمیم گیری درباره این که آیا Order مورد نظر آماده برای تحویل است یا خیر به این Action سپرده شده ، همچنین تصمیم گیری درباره اینکه آیا کاربر دارای آدرس ایمیل جهت ارسال ایمیل می‌باشد یا خیر به این Action سپرده شده است. منطق‌های domain logic و business logic نباید در کلاس‌های presentation همانند Controller‌ها قرار داده شود. تست و نگهداری کدی مثل کد بالا دشوار خواهد بود. Refactoring که باید در این Code اعمال شود Refactor Architecture by Tiers  نام دارد. این Refactoring به توسعه دهنده می‌گوید که منطقی که در لایه‌ی نمایش (Presentation) پیاده کرده را به لایه‌ی Business انتقال دهد. پس از انتقال منطق کد بالا به OrderShippingService ، کد Action ما ساده‌تر می‌شود : 
public RedirectToRouteResult Ship(int orderId)
{
   var status = _orderShippingService.Ship(orderId);
   if (status.Successful)
   {
      return RedirectToAction("Shipped", "Order", new {orderId});
   }
   return RedirectToAction("NotShipped", "Order", new {orderId});
}
پس از انتقال منطق تجاری به محل مناسب خودش تنها مسئولیتی که برای برای Controller باقی مانده این است که کاربر را به کجا Redirect کند. پس از این Refactoring علاوه برا اینکه مسئولیت‌ها در جای مناسب خود قرار گرفتند ، اکنون می‌توان به سادگی منطق کار را بدون تحت تاثیر قرار گرفتن کد‌های لایه‌ی نمایش تغییر داد. 
در آینده به تکنیک‌های ساده سازی Controller‌‌ها خواهیم پرداخت.
نظرات مطالب
بخش اول - آشنایی و شروع کار با Svelte
حجم زیاد برنامه‌های بزرگ که به طور مثال با angular نوشته شده اند همیشه دردسر ساز بوده و کاربرانی که از اینترنت‌های ضعیف‌تر استفاده میکنند را با مشکل مواجه کرده. بخصوص داخل ایران کاربر کم نداریم که این مشکل رو دارند در نتیجه به نظر من معیار بدی نیست برای مقایسه. 
در مورد کم کردن حجم و زمان کدنویسی هم به مراتب به نظر من svelte آمار بسیار مناسبی نسبت به سایر فریم ورک‌های معتبر داره همینطور یادگیری و کارکردن با آن ساده است که به فرایند تولید نرم افزار بسیار کمک میکنه. حداقل برای من این نکته خیلی مهم هستش. 
مورد آخری که میمونه در مورد performance هست که اتفاقا من از دیشب مجددا پس از نوشتن این مقاله در موردش تحقیق کردم ولی هنگام نوشتن این مقاله نخواستم بهش اشاره کنم چرا که شاید به نظر میومد قصد دارم حقیقت رو به شکل دیگه ای نشون بدم.
در واقع  از نظر performance به طور کلی این کامپالر عملکرد به مراتب بهتری داره نسبت به سایر فریم ورک هایی که تا امروز استفاده کرده ایم. چه در استفاده از مموری و چه در اجرای کد تنها به دلیل اینکه از virtual-DOM استفاده نمیکند. در آمار بالا خیلی از پارامتر‌های مهم لحاظ نشده بودند به همین جهت رتبه بندی به این شکل است, چرا که تنها svelte در این لیست یک کامپایلر است و الباقی فریم ورک هستند که وضعیت حدودا برابری با هم دارند. 
پیشنهاد میکنم اگر در مورد مزیت‌های این کامپایلر نیاز به اطلاعات بیشتر داشتید این چند ویدئو رو مشاهده کنید. با جزئیات به این موارد بخصوص در بحث Performance پرداخته اند.
جواب سوال شما در ویدئو اول داده شده و در مورد performance دو ویدئو بعدی دلایل بهتر بودن آن را بیان میکند.
در آخر منظور من به طور قطعی بهتر یا بدتر بودن این کامپایلر نیست ولی با توجه به امکانات و تفاوت هایی که داره ارزش امتحان کردن رو قطعا داره. درمورد تعداد سوال و مشارکتی که گذاشتید حق با شماست این پروژه کاملا یک کار یک نفره است و هنوز جا نیفتاده, شاید هم نیفته به همین جهت من بزرگترین ایراد اون رو نداشتن پشتوانه قوی بیان کردم در قسمت معایب.